Repository navigation
Description
id(web): image upload completing after the document shrinks is silently lost — stale insert position throws, success is logged as "Upload failed"
Evidence (verified this run, main 6f4532a; image-upload.ts unchanged on current main 0c37bb1)
pkgs/id/web/src/image-upload.ts, handleImageUpload (line 209):
- The completion handler looks up the placeholder decoration and falls back to the captured insert position when the placeholder is gone:
const pos = found && found.length > 0 ? found[0].from : insertPos;(line 228). - It then inserts unconditionally at that position:
removeTr.insert(pos, imageNode);(line 235). - If the user edited the document while the upload was in flight and the text around the placeholder was deleted, the placeholder decoration is mapped away and the captured
insertPoscan be beyond the end of the now-smaller document.insertthrows, the throw lands in the shared.catch, and it is logged as[image-upload] Upload failed for <name>: RangeError: Position N out of range(line 239) — even though the upload itself succeeded on the server. The image is never inserted and the user gets no feedback; the blob is orphaned server-side.
Reproduced this run with a targeted vitest probe (real EditorView + real plugin, deferred mocked fetch; probe removed after verification, log in the fleet's probe archive probe-2026-10-27-creator-b2/out.log):
- Doc
paragraph("abcdefghijabcdefghij"), cursor at position 21, paste one PNG → placeholder added at 21. - Before resolving the upload, the whole doc is replaced with
paragraph("x")(doc size 24 → 5; the placeholder is gone). - Resolve the upload successfully → final doc is still just
"x"(no image), and the console warning is[image-upload] Upload failed for a.png: RangeError: Position 21 out of rangeatimage-upload.ts:235.
Control in the same probe: with no shrink, the same upload inserts the image node normally.
Why it matters
Paste an image, keep typing / undo / delete while a slow (10 MB, LAN) upload finishes, and the image silently vanishes — with a console log that actively misdirects debugging by claiming the upload failed. Collab edits from another client can shrink/move the document the same way, so this does not require fast local editing. This is a third, distinct failure mode in the same completion path as #1321 (oversized swallow) and #1323 (multi-image ordering).
Suggested fix
Clamp/validate the fallback position against the current document before inserting (e.g. fall back to a valid position such as the current selection or the end of the document) so a successful upload still lands somewhere sensible; and do not route insertion errors through the upload-failure log message — distinguish "upload failed" from "insertion failed" so the log tells the truth. If the chosen policy is instead to drop the image when its anchor is gone, that must be an explicit, user-visible outcome, not a silent one.
Acceptance criteria
- Regression test: upload resolves after the document has shrunk past the captured insert position (placeholder deleted with the surrounding text) — the image is inserted at a valid in-range position (per the chosen policy), no
RangeError, and the success path is not logged as an upload failure. - The normal path is unchanged: placeholder still present → image inserts at the placeholder's mapped position.
- A genuinely failed upload (non-OK / network error) still removes only its placeholder and logs an upload failure.
- Existing web unit tests pass (
cd pkgs/id/web && bunx vitest run src/image-upload.test.ts, plus the full web suite).
Activity
developing-today commentedon Oct 27, 2026
CODE-474
dezren39 commentedon Oct 27, 2026
🤖 Agent note · filed
18 remaining items
dezren39 commentedon Oct 31, 2026
🤖 Agent note · done
pr_openedqueue/1312-1323-paste-order@6699f099b, head 8a0c784, MERGEABLE). Merge waits for fix(id-web): insert pasted/dropped image batches in file order (#1323) #3194 to land first, then retarget fix(id-web): clamp stale image-upload insert position, distinct insertion-failure logging (#1379) #3202's base tomain.queue/4661-1379-image-upload-clamp· laptop tests: vitest image-upload.test.ts 46/46 pass, ablation verified (new test fails on the unfixed handler)dezren39 commentedon Oct 31, 2026
🤖 Agent note · parked
insertPointfor a block-valid position). PR fix(id-web): clamp stale image-upload insert position, distinct insertion-failure logging (#1379) #3202 is now CLOSED and its branch deleted.main→ CI green + Codex clean → merge fix(id-web): clamp stale image-upload insert position (#1379) #3203 → close id(web): image upload completing after the document shrinks is silently lost — stale insert position throws, success logged as failed #1379 → complete task 4661 as fixed_merged.dezren39 commentedon Oct 31, 2026
🤖 Agent note · picked up
queue/4661-1379-clamp-insertfix(id-web): clamp stale image-upload insert position (#1379) (#3203)
dezren39 commentedon Oct 31, 2026
🤖 Agent note · done
fixed_mergedmain; re-verified fresh: mergeable_state clean, Codex review completed on head2eff04340with no findings, laptopimage-upload.test.ts46/46 (fail-first verified by the opening lane), diff is exactly the clamp +insertPointresolution + distinct insertion-failure logging + shrink regression test.e9bcf49c06f8728b3edebd93db64137aada17f49; id(web): image upload completing after the document shrinks is silently lost — stale insert position throws, success logged as failed #1379 closed by the merge. CI note:id-check.ymlhas nopull_requesttrigger, so the post-merge main run is the CI gate; the only PR check (CodeRabbit) was neutral/skipping (rate-limited, advisory).