fix(execution): give each execution file a unique storage key - #6480
Conversation
Execution file keys were built as
execution/{workspaceId}/{workflowId}/{executionId}/{fileName}, so two files
sharing a display name within one execution resolved to the same key and URL.
The second upload overwrote the first in object storage and updated its
workspace_files row instead of inserting, silently losing a file. Any trigger
that ingests multiple attachments hits this — repeated screenshot names, mail
clients that reuse inline-image names, or a loop emitting the same output name.
generateUniqueExecutionFileKey now allocates a unique directory segment
(.../{executionId}/{uuid}/{fileName}) and uploadExecutionFile uses it, so every
execution file gets its own key. The uniquifier is its own path segment rather
than a filename prefix because presigned URLs carry no content-disposition: the
key's final segment is the name a consumer sees, and a prefix would rename
every download.
The deterministic generator is renamed to generateLargeValuePayloadKey and
takes the payload id instead of a free-form file name, so no user-supplied name
can reach a key without a uniquifier. Its output is unchanged — determinism is
load-bearing there, since the cleanup job matches those keys by LIKE pattern
and the trace store recovers workflowId by segment position.
Every reader tolerates the extra segment: key parsers use parts.length >= 5
with fixed indices, storage providers write a preserved key verbatim, and
local-disk storage already creates the dirname recursively.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview Key generation replaces deterministic Call sites now use the unique helper for Tests shift from “same key replacement” behavior to asserting distinct keys and two metadata inserts for duplicate names, plus updated key-shape expectations. Reviewed by Cursor Bugbot for commit 499162d. Configure here. |
Greptile SummaryThe PR prevents same-named files within one execution from overwriting each other while preserving deterministic keys for large-value payloads.
Confidence Score: 5/5The PR appears safe to merge, with no actionable regressions identified in execution-file storage, access, serving, or cleanup. Unique keys are confined to create-only execution-file paths, while deterministic large-value keys remain unchanged and existing consumers accept or preserve the additional path segment.
|
| Filename | Overview |
|---|---|
| apps/sim/lib/uploads/contexts/execution/utils.ts | Separates unique execution-file key generation from deterministic large-value payload keys without breaking known key parsers. |
| apps/sim/lib/uploads/contexts/execution/execution-file-manager.ts | Allocates a fresh storage key for each directly uploaded execution file, preventing same-name replacement. |
| apps/sim/lib/execution/payloads/store.ts | Uses the payload-specific deterministic generator while preserving the existing large-value storage key format. |
| apps/sim/app/api/files/presigned/route.ts | Gives every presigned execution upload a unique create-only key. |
| apps/sim/app/api/files/multipart/route.ts | Gives every initiated multipart execution upload a unique storage key. |
Reviews (1): Last reviewed commit: "fix(execution): give each execution file..." | Re-trigger Greptile
Summary
execution/{workspaceId}/{workflowId}/{executionId}/{fileName}, so two files sharing a display name in one execution resolved to the same key and URL — the second upload overwrote the first in object storage and updated itsworkspace_filesrow instead of inserting, silently losing a filegenerateUniqueExecutionFileKeynow allocates a unique directory segment (.../{executionId}/{uuid}/{fileName}) anduploadExecutionFileuses it, so every execution file gets its own keygenerateLargeValuePayloadKeyand made it take the payload id instead of a free-form file name, so no user-supplied name can reach a key without a uniquifier. Its output is unchanged — determinism is load-bearing there, since the cleanup job matches those keys by LIKE pattern and the trace store recoversworkflowIdby segment positionCompatibility
parts.length >= 5with fixed indices (matchesExecutionFilePattern,extractWorkspaceIdFromExecutionKey,verifyExecutionFileAccesson both its modern and legacy branches,workflowIdFromStorageKey)sanitizeFileKeypermits the UUID charset in non-leaf segmentsoriginal_nameunique index onworkspace_filesis scoped tocontext = 'workspace', so execution rows now insert per file rather than conflictingexecution/prefixType of Change
Testing
Unit tests cover unique key allocation for duplicate names, the filename-as-final-segment invariant, and the unchanged large-value key. Verified the new tests fail against the old key builder. Full suite and type-check pass.
Checklist