Release the thumbnail queue when a file cannot be read - #2370
Open
enyo wants to merge 1 commit into
Open
Conversation
Contributor
Coverage Report
File Coverage
|
||||||||||||||||||||||||||||||||||||||
`createThumbnail` only handled `FileReader`'s `load` event. A file that had been moved, locked by another process, or was otherwise unreadable since it was dropped fires `error` instead, so the callback was never invoked and `_processThumbnailQueue` kept `_processingThumbnail` set forever. No file added afterwards ever got a thumbnail, and where `transformFile` goes through `createThumbnail` -- which it does as soon as `resizeWidth` or `resizeHeight` is set -- the upload never started either. Hand the error event to the callback, which is what the `img.onerror` path in `createThumbnailFromUrl` already did, so the file is reported with `dictThumbnailError` and the queue carries on. `DropzoneThumbnailCallback` now declares that: `string | Event` rather than `string`, which is what both failure paths have always passed. The two casts that hid it are gone, and with the real signature in place the compiler found `displayExistingFile` emitting the error event as a thumbnail, so a preview whose image URL failed to load ended up with `img.src` set to "[object Event]". Fixes #2365 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
enyo
force-pushed
the
thumbnail-read-error
branch
from
September 13, 2026 11:08
da4efca to
0e3625d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #2365.
The bug
createThumbnailonly ever attachedfileReader.onload. AFileReaderthat fails — the file was moved or deleted after being dropped, the OS refused the read, another process holds a lock, the drive went away — fireserrorinstead, soloadnever runs and the callback is never invoked.That callback is what releases
_processingThumbnail:So the lock is held for the lifetime of the page. Every image added afterwards sits in
_thumbnailQueueand never gets a thumbnail. And becausetransformFileroutes throughcreateThumbnailas soon asresizeWidthorresizeHeightis set, the affected upload never callsdone()either and simply hangs.#2218 added
img.onerrorincreateThumbnailFromUrl, which covers an image that cannot be decoded. This is the stage before it: a file that cannot be read.The fix
Forward the error event to the callback, which is what the
img.onerrorpath already did —_processThumbnailQueueteststypeof dataUrl === "string"and emitsdictThumbnailErrorfor anything else. One failed file now reports an error and the queue carries on;resizeImagefalls back to the untransformed file the same way it does for an undecodable image, so the upload still goes out.The signature was wrong, so it now says what it does
DropzoneThumbnailCallbackdeclareddataUrl: string, but neither failure path has ever passed a string — that is what thetypeofcheck in_processThumbnailQueueexists for. The declaration was only holding because of two casts (callback(e as unknown as string)andimg.onerror = callback as unknown as OnErrorEventHandler). The type is nowstring | Eventand both casts are gone.Making it honest turned up a third call site the casts had been hiding:
displayExistingFilepassed(thumbnail: string) => this.emit("thumbnail", …)straight through, so an image URL that failed to load emitted the error event as the thumbnail and the preview ended up withimg.srcset to"[object Event]"— the same defect #2218 fixed for the queue path. It now leaves the preview alone and still runs the callback.Tests
should emit an error and keep the queue moving if a file can't be read— makesreadAsDataURLdispatcherror, asserts the file is reported withdictThumbnailErrorand_processingThumbnailis back tofalse, then adds a real PNG and waits for itsthumbnailevent. Times out againstmain.should not emit a thumbnail when the image url fails to load— againstmain:expected Event{ isTrusted: true } to be null.