handleFileUpload has no reader.onerror / img.onerror handlers and no client-side 20MB size check — uploads fail silently, large files hang the browser #149
Labels
No labels
blocked:commit-ready-failed
blocked:gate-fetch
blocked:gate-lint
blocked:gate-plan
blocked:gate-research
blocked:needs-attention
blocked:needs-decision
blocked:needs-human
blocked:no-changes
blocked:verify-failed
bug
deferred
feature-request
in-progress
needs-decompose
needs-triage
quickfix
security/medium
triage-approved
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Mark/Kimi_Agent_Brother_10_Digitizing_App#149
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Finding
handleFileUploadinsrc/components/workspace/useWorkspace.ts:638-680does not setreader.onerrororimg.onerror. The FileReader and Image are loaded with onlyonloadhandlers:If the FileReader fails (corrupt file, browser security policy denial, out-of-memory for a multi-hundred-MB image, etc.), nothing happens. The dispatch never fires, the state never changes, the user sees the upload area as if they hadn't dropped a file. No error toast. No UI feedback.
Same for
img.onloadandimg.onerror: if the image data URL is decoded but the image is invalid (truncated PNG, corrupt JPEG), the user gets no feedback.The 20MB size claim on the dropzone (
CanvasArea.tsx:328: "PNG, JPG, SVG, BMP up to 20MB") is also unenforced client-side. A user who drops a 100MB file watches the FileReader chew CPU and then either succeed (silently scaling it to 300px) or fail (with no error toast).Where
src/components/workspace/useWorkspace.ts:638-680—handleFileUploadsrc/components/workspace/CanvasArea.tsx:328Suggested fix shape
Three additions:
Enforce the 20MB size claim client-side before the FileReader. Drop or reject files larger than 20MB with a clear toast:
Set
reader.onerrorto dispatch an error toast on FileReader failure. Theonerrorevent has anerrorproperty that names the reason; show that in the toast for diagnostic value.Set
img.onerrorto dispatch an error toast when the decoded image data is invalid. The Image'sonerrorfires for truncated/corrupt files; the user needs feedback.Enforce a more restrictive MIME type list matching the
acceptattribute. Today,handleFileUploadchecksfile.type.startsWith('image/')which is loose (accepts HEIC, WebP, GIF, AVIF). The Workspace's accept attribute isimage/png,image/jpeg,image/svg+xml,image/bmp; the type check should match.Severity
Medium. The user has no feedback when an upload fails silently. The 20MB claim is unenforced, so a 200MB photo hangs the browser.
Discovered by
Gap analysis (2026-10-02) of upload affordances.
Related
parseDesignFilewas made honest. The upstream upload flow that feeds it the file still has silent failures.Fixed in commit
551f786on origin/main.5 new tests cover the error paths (rejected size, rejected MIME, accepted types, FileReader onerror, img.onerror). Lint clean, typecheck clean, 604/604 tests pass.