claude-code - 💡(How to fix) Fix [Regression] advisor() still breaks sessions post-compaction via parentUuid tree mismatch -- root cause identified, workaround script included [1 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#60523Fetched 2026-05-20 03:56:23
View on GitHub
Comments
0
Participants
1
Timeline
4
Reactions
0
Participants
Timeline (top)
labeled ×4

Root Cause

Issues #53365, #55535, #56515, #49994, #50223 were all auto-closed as duplicates with zero human resolution or fix. This bug is alive. Reporting with the specific root cause not present in any prior issue.

Fix Action

Fix / Workaround

Workaround (until fixed)

Code Example

400 messages.N.content.0: unexpected `tool_use_id` found in `advisor_tool_result` blocks: srvtoolu_XXXXXX.
Each `advisor_tool_result` block must have a corresponding `server_tool_use` block before it.

---

import json, shutil
from pathlib import Path

path = Path('~/.claude/projects/YOUR-PROJECT/YOUR-SESSION.jsonl').expanduser()
shutil.copy2(path, path.with_suffix('.jsonl.bak'))

with open(path) as f:
    lines = f.readlines()

fixed = []
for line in lines:
    if 'srvtoolu_' not in line:
        fixed.append(line)
        continue
    obj = json.loads(line)
    msg = obj.get('message', {})
    content = msg.get('content', []) if msg else []
    if isinstance(content, list):
        new_content = [b for b in content if not (
            isinstance(b, dict) and b.get('type') in ('advisor_tool_result', 'server_tool_use')
            and (b.get('tool_use_id', '') + b.get('id', '')).startswith('srvtoolu_')
        )]
        if len(new_content) != len(content):
            if new_content:
                msg['content'] = new_content
                obj['message'] = msg
                fixed.append(json.dumps(obj) + '\n')
            continue  # drop empty records
    fixed.append(line)

with open(path, 'w') as f:
    f.writelines(fixed)
RAW_BUFFERClick to expand / collapse

Still happening as of May 2026

Issues #53365, #55535, #56515, #49994, #50223 were all auto-closed as duplicates with zero human resolution or fix. This bug is alive. Reporting with the specific root cause not present in any prior issue.

Root cause (new finding)

Claude Code stores conversation turns as a JSONL tree linked by parentUuid. When auto-compaction runs during a long session, it can cause the server_tool_use (advisor call) and its corresponding advisor_tool_result to land on different branches of the conversation tree — i.e. their parentUuid fields point to different ancestors.

When the API reconstructs the message sequence for the next request, it walks a single branch. The advisor_tool_result appears on the path but its paired server_tool_use does not, triggering:

400 messages.N.content.0: unexpected `tool_use_id` found in `advisor_tool_result` blocks: srvtoolu_XXXXXX.
Each `advisor_tool_result` block must have a corresponding `server_tool_use` block before it.

Concretely, in my broken session the first 3 advisor pairs were correctly linked:

  • advisor_tool_result.parentUuid == server_tool_use.uuid (correct)

The 4th pair was broken:

  • server_tool_use uuid=5baa7720-..., parentUuid=cd2695f1-...
  • advisor_tool_result parentUuid=9497d413-... (different parent, different branch)

The session becomes permanently unrecoverable -- every subsequent API call fails 400. /compact makes it worse (it is what caused the split). /rewind cannot reach back far enough once the session is long.

Reproduction

  1. Run a long session (795 JSONL records in mine) with multiple advisor() calls
  2. Let auto-compaction fire mid-session
  3. Next prompt throws 400; every prompt after also throws 400

Workaround (until fixed)

Find the broken session JSONL at ~/.claude/projects/<slug>/<session-id>.jsonl and strip the orphaned blocks:

import json, shutil
from pathlib import Path

path = Path('~/.claude/projects/YOUR-PROJECT/YOUR-SESSION.jsonl').expanduser()
shutil.copy2(path, path.with_suffix('.jsonl.bak'))

with open(path) as f:
    lines = f.readlines()

fixed = []
for line in lines:
    if 'srvtoolu_' not in line:
        fixed.append(line)
        continue
    obj = json.loads(line)
    msg = obj.get('message', {})
    content = msg.get('content', []) if msg else []
    if isinstance(content, list):
        new_content = [b for b in content if not (
            isinstance(b, dict) and b.get('type') in ('advisor_tool_result', 'server_tool_use')
            and (b.get('tool_use_id', '') + b.get('id', '')).startswith('srvtoolu_')
        )]
        if len(new_content) != len(content):
            if new_content:
                msg['content'] = new_content
                obj['message'] = msg
                fixed.append(json.dumps(obj) + '\n')
            continue  # drop empty records
    fixed.append(line)

with open(path, 'w') as f:
    f.writelines(fixed)

After running, claude --resume <session-id> works normally.

Fix direction

Compaction should either:

  • Preserve server_tool_use/advisor_tool_result pairs on the same branch, or
  • Detect and strip orphaned pairs before sending to the API rather than failing hard

Note to triage bot: This is NOT a duplicate of #53365, #55535, or #56515. Those were closed without a fix. This provides the specific root cause (parentUuid tree mismatch during compaction) and a working recovery script not present in any prior report.

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…

Still need to ship something?

×6

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

Back to top recommendations

TRENDING