claude-code - 💡(How to fix) Fix [BUG] Image paste without text creates empty text block; API rejects every subsequent request with 400 [2 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#58667Fetched 2026-05-14 03:42:29
View on GitHub
Comments
2
Participants
2
Timeline
6
Reactions
0
Author
Participants
Timeline (top)
labeled ×4commented ×2

When a user pastes an image into the CLI with no accompanying text, Claude Code persists the user message as [{type: "image", ...}, {type: "text", text: ""}]. On a later turn, the harness adds a cache_control marker to one of those empty text blocks. The Anthropic API rejects the request with HTTP 400:

messages.<N>.content.<i>.text: cache_control cannot be set for empty text blocks

Because the offending message stays in conversation history, every subsequent request 400s with the same error. The session becomes permanently stuck — /compact fails (its outgoing request includes the bad message), and there is no in-CLI way to recover. The only fix today is killing the CLI and hand-editing the session JSONL.

Error Message

API Error: 400 messages.<N>.content.<i>.text: cache_control cannot be set for empty text blocks

Root Cause

Because the offending message stays in conversation history, every subsequent request 400s with the same error. The session becomes permanently stuck — /compact fails (its outgoing request includes the bad message), and there is no in-CLI way to recover. The only fix today is killing the CLI and hand-editing the session JSONL.

Fix Action

Workaround

Externally kill the CLI process, then patch the session JSONL to replace "text": "" with "text": " " (single space) on every offending user event. After resuming, the session works normally.

Sketch:

SESSION=~/.claude/projects/<project-slug>/<session-uuid>.jsonl
cp -p "$SESSION" "$SESSION.bak"
jq -c '
  if (.type == "user" or .type == "assistant") and ((.message.content // []) | type == "array")
  then .message.content |= map(
    if .type == "text" and ((.text // "") == "")
    then .text = " " else . end
  )
  else . end
' "$SESSION" > "$SESSION.tmp" && mv "$SESSION.tmp" "$SESSION" && chmod 600 "$SESSION"

Code Example

messages.<N>.content.<i>.text: cache_control cannot be set for empty text blocks

---



---

API Error: 400 messages.<N>.content.<i>.text: cache_control cannot be set for empty text blocks

---

{
  "uuid": "df37d37a-c629-490e-b0f5-cb648996e3e0",
  "type": "user",
  "message": {
    "role": "user",
    "content": [
      { "type": "image", "source": { "type": "base64", "media_type": "image/png", "data": "..." } },
      { "type": "text", "text": "" }
    ]
  }
}

---

SESSION=~/.claude/projects/<project-slug>/<session-uuid>.jsonl
cp -p "$SESSION" "$SESSION.bak"
jq -c '
  if (.type == "user" or .type == "assistant") and ((.message.content // []) | type == "array")
  then .message.content |= map(
    if .type == "text" and ((.text // "") == "")
    then .text = " " else . end
  )
  else . end
' "$SESSION" > "$SESSION.tmp" && mv "$SESSION.tmp" "$SESSION" && chmod 600 "$SESSION"
RAW_BUFFERClick to expand / collapse

Preflight Checklist

  • I have searched existing issues and this hasn't been reported yet
  • This is a single bug report (please file separate reports for different bugs)
  • I am using the latest version of Claude Code

What's Wrong?

Summary

When a user pastes an image into the CLI with no accompanying text, Claude Code persists the user message as [{type: "image", ...}, {type: "text", text: ""}]. On a later turn, the harness adds a cache_control marker to one of those empty text blocks. The Anthropic API rejects the request with HTTP 400:

messages.<N>.content.<i>.text: cache_control cannot be set for empty text blocks

Because the offending message stays in conversation history, every subsequent request 400s with the same error. The session becomes permanently stuck — /compact fails (its outgoing request includes the bad message), and there is no in-CLI way to recover. The only fix today is killing the CLI and hand-editing the session JSONL.

Environment

  • macOS (Darwin 25.4.0)
  • Claude.app desktop 1.6608.2 (the CLI was invoked from a terminal subprocess of the app's bundled shell)
  • claude --version: (please fill in)
  • Session size at time of failure: ~4.3 MB JSONL, ~507K cached input tokens, ~1,500 events, model claude-opus-4-7

Severity / impact

High in practice. Once it triggers, the session is unrecoverable from within the CLI — no slash command works, /compact makes it worse, and the only path forward without losing the session is process-kill + manual JSONL editing. Users who don't know how to do that will simply lose their session context.

It is also likely to be hitting other users silently — anyone who pastes a screenshot with no accompanying text is a candidate. Worth checking the issue tracker for related reports filed under generic "API errors" labels.

What Should Happen?

Images should send to the remote session or error out in a non-critical way that blocks the user from continuing the session.

Error Messages/Logs

Steps to Reproduce

Reproduction

  1. Start a claude session in a project.
  2. Take a screenshot. Paste it into the prompt without typing any text. Press Enter.
  3. Continue the conversation for a few turns until cache breakpoints shift onto the empty text companion block (in my session this took ~2 minutes of normal use after the paste).
  4. Every assistant turn from that point on returns the synthetic error:
    API Error: 400 messages.<N>.content.<i>.text: cache_control cannot be set for empty text blocks

In my session the failure cascade started 47 seconds after the paste and produced 12 consecutive synthetic-failure turns before I intervened externally.

What the offending event looks like in the JSONL

{
  "uuid": "df37d37a-c629-490e-b0f5-cb648996e3e0",
  "type": "user",
  "message": {
    "role": "user",
    "content": [
      { "type": "image", "source": { "type": "base64", "media_type": "image/png", "data": "..." } },
      { "type": "text", "text": "" }
    ]
  }
}

The empty text: "" block is the bug seed. The harness then adds cache_control on top of it when building a later request.

Expected behavior

One of the following:

  1. Don't emit an empty companion text block. When the user pastes an image with no text, persist just [{type: "image", ...}]. Image-only user messages are valid in the API.
  2. Don't apply cache_control to empty text blocks. When the harness selects cache breakpoints, skip any block where text === "" (or where text is missing/null).
  3. At minimum, self-heal on 400. Detect the cache_control cannot be set for empty text blocks response, prune the offender, and retry once — instead of looping forever with no user-facing recovery path.

(1) is the cleanest fix; (2) is a defense-in-depth net; (3) limits blast radius if either of the first two regresses.

Actual behavior

  • Empty text block is persisted.
  • cache_control ends up on it.
  • API returns 400.
  • Every retry includes the same bad message, so every retry 400s.
  • /compact 400s for the same reason (its own request reads the full history).
  • Synthetic <synthetic> assistant messages with stop_reason: "stop_sequence" accumulate in the transcript. The stop_reason is misleading here — nothing actually triggered a stop sequence; it is the fallback the CLI emits when no real model response was obtained.

Workaround

Externally kill the CLI process, then patch the session JSONL to replace "text": "" with "text": " " (single space) on every offending user event. After resuming, the session works normally.

Sketch:

SESSION=~/.claude/projects/<project-slug>/<session-uuid>.jsonl
cp -p "$SESSION" "$SESSION.bak"
jq -c '
  if (.type == "user" or .type == "assistant") and ((.message.content // []) | type == "array")
  then .message.content |= map(
    if .type == "text" and ((.text // "") == "")
    then .text = " " else . end
  )
  else . end
' "$SESSION" > "$SESSION.tmp" && mv "$SESSION.tmp" "$SESSION" && chmod 600 "$SESSION"

Claude Model

Opus

Is this a regression?

Yes, this worked in a previous version

Last Working Version

No response

Claude Code Version

1.6608.2

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

No response

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

One of the following:

  1. Don't emit an empty companion text block. When the user pastes an image with no text, persist just [{type: "image", ...}]. Image-only user messages are valid in the API.
  2. Don't apply cache_control to empty text blocks. When the harness selects cache breakpoints, skip any block where text === "" (or where text is missing/null).
  3. At minimum, self-heal on 400. Detect the cache_control cannot be set for empty text blocks response, prune the offender, and retry once — instead of looping forever with no user-facing recovery path.

(1) is the cleanest fix; (2) is a defense-in-depth net; (3) limits blast radius if either of the first two regresses.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

claude-code - 💡(How to fix) Fix [BUG] Image paste without text creates empty text block; API rejects every subsequent request with 400 [2 comments, 2 participants]