Skip to content

Conversation

@mbifulco
Copy link

@mbifulco mbifulco commented Oct 18, 2025

What?

The UploadThing storage adapter had two critical bugs that prevented images from loading correctly:

  1. Missing URL field on upload: The handleUpload function only set _key and filename fields from the UploadThing API response, but not the url field. This caused newly uploaded images to have no URL in the database.

  2. Disabled generateURL hook: The disablePayloadAccessControl setting was commented out, which prevented Payload's cloud storage plugin from calling adapter.generateURL() in the afterRead hook. This caused the API to fall back to generating /api/media/file/* URLs instead of using UploadThing CDN URLs (utfs.io), making all images inaccessible.

How?

This PR includes two fixes, and a documentation change:

  1. Set URL on upload (handleUpload.ts): Add data.url = res.data?.url for both main file uploads and image size variant uploads, so newly uploaded files have the correct UploadThing CDN URL stored in the database.

  2. Enable disablePayloadAccessControl (index.ts): Uncomment the disablePayloadAccessControl: true setting when ACL is public-read. This ensures Payload calls adapter.generateURL() when serializing documents, which generates the correct UploadThing CDN URLs for existing files.

  3. Document JWT token requirement (README.md): Add documentation clarifying that this plugin requires UploadThing tokens in JWT format (starting with ey), not the sk_ secret key format. I spent a while trying to figure out why uploadThing wasn't working - their dashboard has 2 quick-copyable different formats for API key, and I was using the wrong one!

Testing

  • Verified new uploads correctly store UploadThing CDN URLs in database
  • Confirmed API responses return https://utfs.io/f/{key} URLs instead of /api/media/file/* URLs
  • Tested that images load correctly from the UploadThing CDN

Fixes: Images returning 400 errors and failing to load from /api/media/file/*

The handleUpload function was only setting _key and filename fields
from the UploadThing API response, but not the url field. This caused
uploaded images to fail loading because Payload couldn't find the
proper CDN URL.

This fix adds data.url = res.data?.url for both main file uploads
and image size variant uploads.

Fixes: Images returning 400 errors when trying to load from /api/media/file/
Closes: #[ISSUE_NUMBER]
@mbifulco mbifulco requested a review from denolfe as a code owner October 18, 2025 01:47
…ment JWT token requirement

This commit adds two critical fixes:

1. Uncomment disablePayloadAccessControl for public-read ACL
   - When disabled/undefined, Payload cloud storage plugin does not call
     adapter.generateURL() in the afterRead hook
   - Without this, Payload falls back to /api/media/file/* URLs instead
     of using the UploadThing CDN URLs (utfs.io)
   - This makes images inaccessible since files are stored on UploadThing

2. Document JWT token format requirement in README
   - UploadThing plugin requires tokens in JWT format (starting with "ey")
   - Using sk_ format tokens will cause upload failures
   - Added clear documentation to prevent this common mistake
@mbifulco
Copy link
Author

Just wanted to note: we're using this change in production and it seems to be working great so far!

@mbifulco
Copy link
Author

mbifulco commented Nov 11, 2025

Hi team - anything I can do to help here? We have been using this change via pnpm patch without issue. If I can update the PR to help the process along, I'm happy to do so!

edit: created a little tutorial on how to use pnpm-patch in case it helps anyone who finds themselves here

@jhb-dev
Copy link
Contributor

jhb-dev commented Nov 11, 2025

Hi @mbifulco, point 1 (“Missing URL field on upload”) appears to be related to this issue I opened earlier today: #14562

After reviewing Payloads' source code, it looks like none of the official storage adapters set the url field within the getHandleUpload function, so this is not specific to the UploadThing adapter but rather a general pattern or issue across all adapters.

@mbifulco
Copy link
Author

Your PR looks great @jhb-dev - appreciate the video, that makes it super clear. Anything I can do to help with that issue at the moment?

@github-actions github-actions bot added the stale label Dec 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

2 participants