API Error: 400 messages: text content blocks must be non-empty on session resume when assistant turn contained only tool_use blocks
Summary
When resuming a Claude Code session, the Anthropic API rejects the replayed conversation history with:
API Error: 400 messages: text content blocks must be non-empty
This makes the session unresumable and is especially severe for remote/headless sessions where starting a new session is not trivial.
Root cause
The harness serializes assistant turns that contained only tool_use blocks (no accompanying text) by adding an empty "" text block. The Anthropic API rejects message arrays containing {"type": "text", "text": ""}.
An affected assistant turn in the session JSONL looks like:
{
"role": "assistant",
"content": [
{"type": "text", "text": ""},
{"type": "tool_use", "id": "...", "name": "...", "input": {}}
]
}
The empty text block is invalid per the API contract. It should be omitted entirely when there is no text.
Steps to reproduce
- Start a Claude Code session.
- Trigger a turn where the model responds with one or more tool calls and no text (e.g. a rapid retrieval-only response with no preamble).
- End or interrupt the session.
- Attempt to resume the session via
--resumeor by reopening the project.
Result: API Error: 400 messages: text content blocks must be non-empty
Expected: Session resumes normally, or the harness strips/skips empty text blocks before replaying history.
Workaround
Strip empty text blocks from the session JSONL manually before resuming:
import json
path = "~/.claude/projects/<project>/<session-id>.jsonl"
fixed = []
with open(path) as f:
for line in f:
entry = json.loads(line)
msg = entry.get("message", {})
if msg.get("role") == "assistant":
content = msg.get("content", [])
msg["content"] = [
b for b in content
if not (b.get("type") == "text" and b.get("text", "").strip() == "")
]
fixed.append(entry)
with open(path, "w") as f:
for entry in fixed:
f.write(json.dumps(entry) + "\n")
Suggested fix
When serializing assistant turns for the API messages array, skip any {"type": "text", "text": ""} blocks. Alternatively, validate the content array before sending and strip empty text blocks at the API call site.
Environment
- Claude Code (CLI)
- Affected on session resume (
--resumeor project reopen) - Not triggered by a specific model version — the issue is in harness serialization
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗