plugin-cloud-storage: upload with a focal point set before first save fails when the adapter returns a new filename (e.g. Vercel Blob addRandomSuffix)
Maintainers usually reply within 1 day
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 35/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Stale
- Tech stack
- next.js, node.js, typescript
Research direction
Read packages/plugin-cloud-storage/src/hooks/afterChange.ts and packages/plugin-cloud-storage/src/hooks/generateFileData.ts, focusing on the nested update and upload edit parsing described in the issue. Reproduce with a Vercel Blob adapter using addRandomSuffix: true and a focal point set before the first save. Done means the create succeeds without fetching the pre-suffix filename; an open linked pull request already addresses this issue.
Written by the indexing model from the issue text.
Description
Describe the Bug
Creating a document in an upload collection fails with a 500 ("An unknown error has occurred" in the admin) when both of these are true:
- the storage adapter's
handleUploadreturns metadata such as a newfilename.@payloadcms/storage-vercel-blobdoes this whenaddRandomSuffix: true. - the editor sets a focal point before the first save, so the admin sends
?uploadEdits[focalPoint][x]=…&[y]=….
Server log:
There was an error while uploading files corresponding to the collection media with filename IMG_9210 2.jpeg:
There was a problem while uploading the file. Failed to fetch file from https://<store>.public.blob.vercel-storage.com/IMG_9210%202.jpeg
at hooks.afterChange.adapter …
Cause
In plugin-cloud-storage/src/hooks/afterChange.ts, when handleUpload returns metadata, the hook stores it with a nested req.payload.update({ …, req }) on the same req. Before that call it clears req.file and req.payloadUploadSizes, but leaves req.query.uploadEdits in place.
Inside the nested update, generateFileData → parseUploadEditsFromReqOrIncomingData reads req.query.uploadEdits.focalPoint again. That value came from the query string, so x and y are strings ("37"). shouldReupload compares them strictly with the stored numbers (37), finds them unequal, and treats it as a focal-point edit. It then calls getExternalFile on originalDoc.url, which still points at the pre-suffix filename. That object doesn't exist, so the fetch 404s, the error is thrown, and the create rolls back.
Dragging the file in without setting a focal point works, because no uploadEdits is sent.
Suggested fix (either one works)
- In
afterChange.ts, also clear the consumed edits before the nested update, next toreq.file = undefined:if (req.query) delete req.query.uploadEdits - In
generateFileData.ts, convert to numbers inshouldReupload(Number(uploadEdits.focalPoint.x) === fileData.focalX), so query-string values compare correctly.
Workaround we shipped: a collection beforeOperation hook that deletes req.query.uploadEdits when operation === 'update' && req.context?.skipCloudStorage.
Link to the code that reproduces this issue
Our project repo is private, but the Local API snippet below reproduces it in any project with the Vercel Blob adapter configured with addRandomSuffix: true. Happy to put together a standalone repro repo if that would help.
Reproduction Steps
- Configure
vercelBlobStorage({ collections: { media: true }, addRandomSuffix: true, token })on an upload collection withfocalPoint: true(the default). - In the admin, create a new media doc, choose a file, and move the focal point away from the center.
- Save. The request returns a 500 and the server logs
Failed to fetch file from …/<unsuffixed filename>.
The same thing happens through the Local API:
await payload.create({
collection: 'media',
data: {},
file: { data, mimetype: 'image/jpeg', name: 'test image.jpeg', size: data.length },
req: { query: { uploadEdits: { focalPoint: { x: '37', y: '62' } } } },
})
With numeric x/y equal to the stored values, it succeeds.
Which area(s) are affected?
plugin: cloud-storage, area: uploads
Environment Info
payload: 3.90.2 (code unchanged in plugin-cloud-storage 3.91.0)
@payloadcms/storage-vercel-blob: 3.90.2
@vercel/blob: 2.3.1
next: 16.3.6
node: 24
db: @payloadcms/db-postgres
- Dominant language
- TypeScript
- Stars
- 45.5k
- Forks
- 4.3k
- Avg merge
- 1d 19h
- Merged PRs (30d)
- 295
Getting set up
Starts the project's dev container in your browser, under your own GitHub account.
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from payloadcms/payload
-
Bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
payloadcms/payload#19060 ·
Maintainers usually reply within 1 day
-
WhereBuilder throws "Cannot read properties of undefined (reading 'value')" when the list URL filters on a field that is not filterablePossibly taken A pull request linked to this issue is open or already merged. OpenBug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
payloadcms/payload#19002 · 1 comment ·
Maintainers usually reply within 1 day
-
Regression: `findByID` with `disableErrors: true` throws `NotFound` for null/empty ids since 3.91.0Possibly taken @Krrishkumar125 claimed this 4 days ago. Openarea: core Bug invalid-reproduction status: needs-triage v3
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
payloadcms/payload#18935 · 1 comment ·
Maintainers usually reply within 1 day
-
bug: Local API call with req resets req.fallbackLocale to defaultLocalePossibly taken A pull request linked to this issue is open or already merged. Openarea: core Bug created-by: Contributor
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
payloadcms/payload#18768 ·
Maintainers usually reply within 1 day
-
[plugin-multi-tenant] Infinite redirect loop with custom tenantFieldName and isGlobal collectionsPossibly taken A pull request linked to this issue is open or already merged. OpenBug plugin: multi-tenant status: needs-triage v3
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
payloadcms/payload#18690 ·
Maintainers usually reply within 1 day
All issues in payloadcms/payload
Similar issues
-
Native Web
Difficulty 1/5 Under an hour Newbie friendliness 80/100
digitalfabrik/integreat-app#4517 ·
Maintainers usually reply within 2 days
-
TLS guide repeats certs/ in the certificate directoryPossibly taken @maheshrijal claimed this today. Open
Difficulty 1/5 Under an hour Newbie friendliness 92/100
prometheus/docs#3156 ·
Maintainers usually reply within 2 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 83/100
medusajs/dtc-starter#90 ·
Maintainers usually reply within 1 day
-
usability
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
inmanta/web-console#7632 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
zaidmukaddam/scira#261 ·