Skip to content

bug(claude): a word-wrapped [Image #N] pill makes every image prompt fail as absorption-timeout #1113

Closed

Description

@Juliusolsson05

Symptom

Sending a prompt with an attachment sometimes ends with the banner

The prompt was never submitted — it is sitting unsent in the agent's composer. Resend is blocked so it does not double up.

The prompt text and the image are in Claude's composer. Enter was never written.

Evidence

Debug bundle 2026-09-21T22-09-23-834-168b7a32 (owner note: "Sending logic broke"):

22:06:59.341  submit.begin   {provider: claude, source: with-images}
22:07:00.360  input.write    {origin: delivery, writes: 3, bytes: 178}
22:07:04.369  submit.result  {ok:false, code:"absorption-timeout", stage:"absorption",
                              bodyWritten:true, enterWritten:false, retryable:false, durationMs:5027}

The recorded composer at that instant (trace/screen/tail-samples.jsonl, seq 21):

❯ Lets figure out why this is happening wiht opencode [Image
  #1]

Root cause

imagePlaceholderCount (src/shared/claude/pasteConfirm.ts) matches
/\[Image #\d+\]/ against the raw composer text. The TUI word-wraps the
composer to the pane width, and [Image #1] has exactly one internal space —
the only place a word wrap can break it. When the pill straddles the wrap
column the count never rises, pollClaudeImagesAbsorbed polls for its full 5 s,
and delivery returns absorption-timeout with retrySafe: false.

Counted over every frame in the bundle:

frames literal match whitespace-normalized
seq 21–23 (the failing send) 0 1
seq 51–59 (the send that worked) 1–2 1–2

Same pane, same session, same width, ten minutes apart. The only difference is
where the space fell — which is why this looks intermittent instead of broken.

The text half of the same protocol, pasteAbsorbedVia, already compares against
normalizeWhitespace(screen). That is exactly why wrapped text sends never had
this problem. The image path never got the same treatment.

Blast radius

Every prompt with an attachment, on any pane narrow enough for the pill to wrap.
retrySafe: false is correct given the failure (orphaned pills cannot be rolled
back), so the user has to clear the agent's composer by hand before sending again.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions