Skip to content

id(web): image upload completing after the document shrinks is silently lost — stale insert position throws, success logged as failed #1379

Closed

Description

@dezren39

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 insertPos can be beyond the end of the now-smaller document. insert throws, 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 range at image-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

  1. developing-today commented on Oct 27, 2026

    @developing-today
    Contributor
  2. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    queue/queuedHas a queued task in the Supabase agent queue
    source/creatorFiled by the new-issue-creator job
    task/triageQueue packet kind: verify, reproduce, and route this item
    priority/normalOrdinary priority (the default)
    app/idPackage/app: id (pkgs/ or apps/ in this repo)
    effort/sSmall: expected under 2 hours of work
    on Oct 27, 2026
  3. dezren39 commented on Oct 27, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

  4. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Oct 31, 2026
  5. 18 remaining items

  6. dezren39 commented on Oct 31, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done

  7. dezren39 commented on Oct 31, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · parked

  8. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    on Oct 31, 2026
  9. dezren39 commented on Oct 31, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/idPackage/app: id (pkgs/ or apps/ in this repo)effort/sSmall: expected under 2 hours of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/normalOrdinary priority (the default)source/creatorFiled by the new-issue-creator jobtask/triageQueue packet kind: verify, reproduce, and route this item

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions