Skip to content

Fix file uploads with non-ASCII filenames - #2876

Open
jxnata wants to merge 3 commits into
instantdb:mainfrom
jxnata:fix-upload-non-ascii-filenames
Open

Fix file uploads with non-ASCII filenames#2876
jxnata wants to merge 3 commits into
instantdb:mainfrom
jxnata:fix-upload-non-ascii-filenames

Conversation

@jxnata

@jxnata jxnata commented Aug 24, 2026

Copy link
Copy Markdown

Summary

  • uploadFile() sent the file's path in a raw HTTP header, but header values must be ISO-8859-1. Filenames with accents (e.g. macOS's NFD-decomposed café.txt, where à is a + a combining grave accent) fall outside that range, so fetch() throws a TypeError before the request even goes out. content-disposition has the same problem.
  • deleteFile() and getDownloadUrl() already avoid this by sending the filename as an encoded query param — uploadFile() was the odd one out. The same bug exists in three places: the core SDK, the admin SDK, and the dashboard's storage explorer (each with its own server handler).
  • Fix: clients now send path (and content-disposition) as an encoded query param, and only mirror them into a header when the value is safe to encode as ISO-8859-1. Servers now merge headers and query params (query params win) when reading these, matching how file-delete / signed-download-url-get already read theirs. This keeps compatibility in both directions: newer clients still work against servers that only read headers, and older clients still work against the updated servers.

Client

  • client/packages/core/src/StorageAPI.tsuploadFile
  • client/packages/admin/src/index.tsStorage.uploadFile
  • client/packages/components/src/components/explorer/inner-explorer.tsx — dashboard storage explorer upload (this is the path that breaks when uploading a file from the dashboard UI)

Server

  • server/src/instant/storage/routes.cljupload-put
  • server/src/instant/admin/routes.cljupload-put
  • server/src/instant/dash/routes.cljupload-put

Docs

  • client/www/app/docs/http-api/page.md — updated the curl example to use the query param form.

Test plan

  • pnpm run build-packages (typecheck across all client packages) passes
  • pnpm run check-format passes on the changed files
  • pnpm run test — existing suite passes (206 tests, 1 pre-existing unrelated failure from a missing local Playwright browser binary)
  • Added server/test/instant/storage/routes_test.clj covering that upload-put reads path from query params, still falls back to the legacy header, and that query params win when both are present
  • Added a regression case to storage-impersonation-test in server/test/instant/admin/routes_test.clj uploading a file with an accented filename via query params

uploadFile() sent the file path in a raw HTTP header, but headers only
accept ISO-8859-1. Filenames with accents (e.g. macOS's NFD-decomposed
"café.txt") fall outside that range, so fetch() throws before the
request is even sent. deleteFile() and getDownloadUrl() already avoided
this by sending the path as an encoded query param; upload was the odd
one out, across the core SDK, admin SDK, and the dashboard's storage
explorer. content-disposition has the same issue and gets the same fix.

Clients now send path/content-disposition as an encoded query param and
only mirror them into headers when the value is ISO-8859-1-safe, so
requests to older self-hosted servers keep working. The corresponding
server upload handlers merge headers and query params (query params
win), matching how file-delete and signed-download-url-get already read
their params, so newer clients keep working against older servers too.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PQ8ju4b8aRBBpVTXsfxdSp
@coderabbitai

coderabbitai Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: de16b2a4-c00d-482a-88d6-bff2758117a9

📥 Commits

Reviewing files that changed from the base of the PR and between 83f2c97 and 9a21e1a.

📒 Files selected for processing (8)
  • client/packages/admin/src/index.ts
  • client/packages/components/src/components/explorer/inner-explorer.tsx
  • client/packages/core/src/StorageAPI.ts
  • server/src/instant/admin/routes.clj
  • server/src/instant/dash/routes.clj
  • server/src/instant/storage/routes.clj
  • server/test/instant/admin/routes_test.clj
  • server/test/instant/storage/routes_test.clj
💤 Files with no reviewable changes (5)
  • client/packages/core/src/StorageAPI.ts
  • server/test/instant/storage/routes_test.clj
  • server/test/instant/admin/routes_test.clj
  • client/packages/admin/src/index.ts
  • client/packages/components/src/components/explorer/inner-explorer.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • server/src/instant/dash/routes.clj
  • server/src/instant/admin/routes.clj
  • server/src/instant/storage/routes.clj

Included review availability: Your plan provides up to 10 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

Storage upload clients now send encoded path and metadata query parameters. Servers prioritize these parameters while retaining legacy header support. Header mirroring is limited to ISO-8859-1-safe values. Tests cover precedence and non-ASCII filenames.

Changes

Storage upload query parameters

Layer / File(s) Summary
Client upload transport
client/packages/admin/src/index.ts, client/packages/core/src/StorageAPI.ts, client/packages/components/src/components/explorer/inner-explorer.tsx, client/www/app/docs/http-api/page.md
Upload URLs now include encoded app_id, path, and optional content-disposition parameters. Header mirroring occurs only for ISO-8859-1-safe values. The HTTP API example uses query parameters.
Server upload parameter resolution
server/src/instant/storage/routes.clj, server/src/instant/dash/routes.clj, server/src/instant/admin/routes.clj
Upload routes merge keywordized headers with request parameters. Query parameters take precedence, while legacy headers remain supported.
Upload compatibility validation
server/test/instant/storage/routes_test.clj, server/test/instant/admin/routes_test.clj
Tests cover query parameter precedence, legacy header fallback, and uploads with non-ASCII filenames.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 9a21e

The change moves non-ASCII upload metadata into encoded query parameters while preserving legacy header compatibility across clients and servers; no actionable merge-blocking risk remains after normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant UploadRoute
  participant StorageCoordinator
  Client->>UploadRoute: PUT request with encoded query parameters and safe legacy headers
  UploadRoute->>UploadRoute: Merge query parameters over keywordized headers
  UploadRoute->>StorageCoordinator: Upload file with resolved path and metadata
  StorageCoordinator-->>Client: Upload response
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 3 files. (5 skipped: 5… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The description clearly explains the non-ASCII filename upload bug, the client and server changes, compatibility behavior, documentation updates, and test coverage.
Title check ✅ Passed The title clearly and concisely identifies the main change: fixing file uploads for non-ASCII filenames.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 3 files. (5 skipped: 5 unsupported.)


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

jxnata and others added 2 commits August 24, 2026 21:07
Documents isHeaderSafe and the touched uploadFile/upload-put functions
across the client and server, matching the docstring style already used
by their siblings (e.g. the other functions in storage/coordinator.clj).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PQ8ju4b8aRBBpVTXsfxdSp
The rationale for the header/query-param split is already in the PR
description; the code doesn't need to restate it inline.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PQ8ju4b8aRBBpVTXsfxdSp
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant