Knowledge Base Uploads: Large Files & Filenames With Spaces Now Work
AI Agent Knowledge Base uploads were silently failing for two classes of input: any document larger than ~5 MB, and any filename containing spaces or unicode characters. Both are fixed on dev. This post documents what broke, why, and how the fix changes the upload path.
What was broken
Two stacked bugs, both in the path lib/hooks/useAgentDocuments.ts → app/api/upload/route.ts:
Bug 1 — 413 Payload Too Large on files over ~5 MB
Knowledge Base uploads were POSTed to the Next.js /api/upload route, which runs on Amplify Compute (AWS Lambda). Lambda caps request bodies at roughly 5 MB, so any 10–20 MB PDF was rejected by the infrastructure before the route handler ran. The route's own 50 MB self-check never fired because no Lambda invocation happened. Users saw UPLOAD_ERROR: Upload failed with status 413.
This was reported in SUP-00072. An earlier attempt at a fix addressed file-type handling and S3 cleanup on delete — neither of which was the real problem.
Bug 2 — NoSuchKey on filenames with spaces or unicode
The same route extracted the S3 key out of the presigned URL's pathname:
key = new URL(uploadUrl).pathname.substring(1).split("?")[0]
URL.pathname returns the URL-encoded form, so letter of intention.pdf was stored in the database as letter%20of%20intention.pdf. S3 held the object under the literal (decoded) name. Background-tasks then called GetObject(encoded_key) → NoSuchKey → document stuck in FAILED after 4 retry attempts.
Five historical FAILED records on dev traced back to this encoding mismatch — including Usman's letter of intention.pdf (82 KB, well under any size limit).
What changed
aiqlick-frontend/lib/hooks/useAgentDocuments.ts now uploads directly from the browser to S3 via a short-lived presigned PUT URL issued by the main backend's existing upload.generateUploadUrl mutation. The Next.js proxy is bypassed entirely for this path.
Specifics:
- No Amplify middleman → no 5 MB body cap. Files up to 50 MB (client-side guard) are accepted; S3's single-PUT ceiling is 5 GB.
objectPathis the authoritative key — the backend returns it un-encoded, and we use it directly. No more URL-decoding bugs.- Client-side size guard with a clear
TWToasterror if the user picks a file > 50 MB. - Progress bar preserved via XMLHttpRequest's
upload.progressevent. - Do NOT send
Authorizationon the S3 PUT — the presigned URL encodes its own SigV4 signature, and adding anAuthorizationheader makes S3 reject the request.
A new mutation module lives at graphql/operations/upload/mutations.ts (GENERATE_UPLOAD_URL).
Scope — Knowledge Base only
The Next.js /api/upload route is NOT deleted. About 20 other features still use it — CV uploads, profile avatars, the admin media manager, messaging attachments, contacts import, buzzfeed, and support ticket attachments. Most of those deal with files small enough that the 5 MB cap isn't hit in practice, but each one is a candidate for the same migration.
If you're adding a new feature that uploads files, default to the direct-to-S3 flow. See the updated File Upload Flow guide.
For users
Please retest on dev.aiqlick.com:
- Upload a PDF > 10 MB — should succeed, process, and reach
TRAINED. - Upload a file whose name contains spaces — should process successfully (no
NoSuchKeyin background-tasks logs). - Select multiple files at once — each uploads sequentially with its own progress bar.
Documents that were left in FAILED from before the fix are not recoverable — their database records hold the URL-encoded key. Delete and re-upload once dev is green.
Files changed
aiqlick-frontend/lib/hooks/useAgentDocuments.ts— rewrite upload step to use presigned PUT +objectPathkeyaiqlick-frontend/graphql/operations/upload/mutations.ts— new, containsGENERATE_UPLOAD_URL- Documentation: File Upload Flow, Amplify Hosting, AI Agent — RAG Document Management
Verification
npx tsc --noEmit— cleannpm run build— full Next.js build passes- Shipped to dev as commit
a813cccc
SUP-00072 is READY_FOR_RETEST on dev.