Skip to content

[bug]: HEIC upload fails with Invalid signature when .heic file is reported as image/heif #1287

Description

Provide environment information

System:
  OS: Linux 6.19 Fedora Linux Asahi Remix 44 (KDE Plasma Desktop Edition)
  CPU: (10) arm64 unknown
  Memory: 20.42 GB / 30.85 GB
  Container: Yes
  Shell: 5.3.9 - /bin/bash
Binaries:
  Node: 22.22.2 - /home/fedora/Desktop/codex-desktop-linux/codex-app/resources/node-runtime/bin/node
  npm: 10.9.7 - /home/fedora/Desktop/codex-desktop-linux/codex-app/resources/node-runtime/bin/npm
  bun: 1.3.14 - /home/fedora/.bun/bin/bun
Browsers:
  Firefox: 150.0
  Firefox Developer Edition: 150.0
npmPackages:
  @uploadthing/svelte: ^7.3.3 => 7.3.3
  typescript: ^5.9.3 => 5.9.3
  uploadthing: ^7.7.4 => 7.7.4

Describe the bug

I am using @uploadthing/svelte with SvelteKit and an UploadDropzone. Uploading normal images works, but uploading a .heic file fails at the UploadThing ingest PUT request with 400 Bad Request.

The selected file is:

  • name: 20260625_144458.heic
  • size: 5741430
  • browser-reported type: image/heif

The generated ingest URL contains:

x-ut-file-name=20260625_144458.heic
x-ut-file-size=5741430
x-ut-file-type=image%252Fheif
x-ut-slug=unifiedUploader

The PUT request goes to:

https://fra1.ingest.uploadthing.com/<file-key>?expires=...&x-ut-identifier=...&x-ut-file-name=20260625_144458.heic&x-ut-file-size=5741430&x-ut-file-type=image%252Fheif&x-ut-slug=unifiedUploader&x-ut-content-disposition=inline&signature=...

It reaches 100% upload progress, then UploadThing returns:

{
  "error": "Failed to verify URL: Invalid signature"
}

Expected behavior: either the HEIC/HEIF file uploads successfully, or the client rejects it before upload with a clear unsupported-file-type error.
Actual behavior: the file appears to upload to 100%, then ingest rejects the signed URL as invalid.
This looks like it may be related to URL/signature canonicalization for the file MIME type. In particular, the query param appears as image%252Fheif, which decodes once to image%2Fheif and twice to image/heif.

Link to reproduction

Screencast_20260702_114226.mp4

To reproduce

  1. Create a file route that accepts images, for example:
export const ourFileRouter = {
  unifiedUploader: f({
    image: {
      maxFileSize: "512MB",
      maxFileCount: 20,
    },
  }).onUploadComplete(async ({ file }) => {
    console.log("Upload complete", file.ufsUrl);
  }),
} satisfies FileRouter;
  1. Use @uploadthing/svelte with createUploader("unifiedUploader") and .

  2. Select a .heic image where the browser reports the file MIME type as image/heif.

  3. Observe the ingest PUT request.

  4. The upload progress reaches 100%, but the PUT request returns:

{
  "error": "Failed to verify URL: Invalid signature"
}

Additional information

Additional Information

Client logs from onBeforeUploadBegin:

{
  count: 1,
  files: [
    {
      name: "20260625_144458.heic",
      size: 5741430,
      type: "image/heif"
    }
  ]
}

The same UploadThing route works for normal JPEG/PNG/WebP uploads.
This may be specific to .heic files that the browser reports as image/heif, or to the double-encoded x-ut-file-type=image%252Fheif query param used in the signed ingest URL.

👨‍👧‍👦 Contributing

  • 🙋‍♂️ Yes, I'd be down to file a PR fixing this bug!

Code of Conduct

  • I agree to follow this project's Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions