API 400 on paste-image-without-text: empty text block persists in transcript, breaks all subsequent turns
Pasting image without accompanying text produces empty text block → API 400 on next turn
Summary
When the user pastes an image into Claude Code 2.1.x without typing any accompanying text, the resulting user message's content array contains an image block followed by an empty text block ({"type":"text","text":""}). The message sends successfully the first time, but every subsequent turn in that session re-sends the full transcript to the API, which rejects it with:
400: messages: text content blocks must be non-empty
(older variant on earlier paste-failures also produces: cache_control cannot be set for empty text blocks)
The session becomes unusable until the transcript JSONL file is manually repaired on disk.
Affected version
Claude Code 2.1.112 (current as of 2026-04-18). Likely all 2.1.x — bug observed since at least 2.1.90.
Reproduction
- Open any Claude Code session
- Paste an image (screenshot, copied file, etc.) into the prompt
- Hit enter WITHOUT typing any text
- (First turn may succeed)
- Send any further user message
- API rejects with 400 "text content blocks must be non-empty"
Evidence from our transcripts
After repairing two sessions, the corrupt blocks all matched this pattern:
{
"role": "user",
"content": [
{"type": "image", "source": {...}},
{"type": "image", "source": {...}},
{"type": "text", "text": ""} ← bug
]
}
Sometimes one image, sometimes 2+, always with a trailing empty text block.
Expected behavior
Either:
- A: client strips the empty text block before adding to transcript (preferred)
- B: client inserts a placeholder text block with minimal content (e.g.,
"(image attachment)") - C: API accepts empty text blocks on user messages when the array contains non-empty blocks
Impact / frequency
Single user, 10 agents on one host. Our most-paste-heavy agent hit this 8+ times in one day (running Sprint 14 review with screenshots). Each occurrence requires manual intervention on the JSONL file. We've built an auto-repair hook as a stopgap but the bug is in the client serialization, not the transcript.
Environment
- macOS 15.x (Darwin 25.x)
- Apple Silicon (M4)
- Terminal.app and iTerm2 both affected
- Codeman (web UI) sessions also affected
Workarounds currently in use
- Manual: always type at least one character alongside pasted images (easy to forget)
- Automated: UserPromptSubmit hook that scans the transcript for empty text blocks and strips them before each API call (working, but shouldn't be necessary)
Root-cause hypothesis
The paste handler likely always appends a trailing text block to the content array regardless of whether text was entered. If the text input is empty, the block is added with text: "" instead of being omitted.
Suggested fix
In the message serialization path, if the content array would end with an empty text block AND contains at least one non-text block before it, drop the empty text block before serialization.
This issue has 8 comments on GitHub. Read the full discussion on GitHub ↗