Session repair — orphan tool_use → 400 error

Symptom

Anthropic API returns 400 with message:

“tool_use ids were found without tool_result blocks immediately after”

Session becomes unusable. New messages also fail.

Root cause

Conversation has an assistant message containing tool_use block(s) that lack matching tool_result block(s) in the next user message. API requires strict pairing.

Causes:

  • Sub-agent spawned a tool call that never returned (timeout / kill)
  • Manual session edit removed a tool_result
  • Server crash mid-tool-execution
  • Race condition in MCP server

Fix (manual)

  1. Locate session file:

    /var/www/<workspace>/.workspace/sessions/<sessionId>/messages.jsonl
  2. Find offending assistant message:

    tail -50 messages.jsonl | jq 'select(.role=="assistant") | .content[] | select(.type=="tool_use")'
  3. Two repair strategies:

    A. Drop the orphan turn — remove the assistant message AND any partial user tool_result follow-up:

    # back up first
    cp messages.jsonl messages.jsonl.bak
    # delete last N lines starting from orphan turn
    head -n <line-before-orphan> messages.jsonl.bak > messages.jsonl

    B. Inject empty tool_result — append a synthetic user message with tool_result for each orphan tool_use_id:

    {"role":"user","content":[{"type":"tool_result","tool_use_id":"toolu_xxx","content":"[truncated]"}]}
  4. Restart Claude Code session to reload session file.

Fix (automated, in code)

Post-1.3.7 fork-query patch 0fdffa0 adds automatic stripping of dangling tool_use blocks before the API call:

// agentLoop.ts (paraphrased)
function stripDanglingToolUse(messages) {
  // walk pairs, drop assistant tool_use without follow-up tool_result
}

This runs every API call. Future orphans are auto-handled — but pre-existing corrupt sessions still need manual repair.

Prevention

  • Never kill Claude Code mid-tool-call without grace period
  • MCP servers must always return result (even on error)
  • Sub-agent timeouts should auto-emit [timeout] tool_result
  • ELC release 1.3.7 + post-release patches — see elc-release-1.3.7
  • Saved memory: session-messages-repair-playbook (always-on)