[BUG] Resume drops expanded Skill body when Skill is batched with another tool call

Status Fixed / completed
Reported on v2.1.220
Maintainer reply None cached
Activity 1 comment · opened Jul 28, 2026 · closed Aug 25, 2026

Preflight Checklist

  • [x] I searched existing issues.
  • [x] This is a single bug report.
  • [x] I reproduced this with the latest Claude Agent SDK and its bundled Claude Code CLI.

What's Wrong?

When the model invokes the built-in Skill tool in parallel with another tool, the expanded skill body is available during the live turn but is missing from the model context after resume.

The JSONL file still contains the expanded skill body. It is not truncated or deleted. The body is written as a synthetic user text message on a sibling parentUuid branch, while subsequent conversation continues from the other parallel tool's result. Resume reconstructs one active chain and omits the skill-body branch.

The ordinary Skill tool result (for example, Launching skill: example_skill) remains visible after resume, which makes the loss easy to miss. Only the separate expanded Skill body disappears.

This is model-context loss, not a transcript-storage or UI-rendering issue. I captured the raw /v1/messages request produced during resume and confirmed that the body is absent before the request reaches any model.

Minimal Topology

A parallel assistant response contains:

Skill("example_skill")
Read("/tmp/example_rules.md")

Claude Code writes the relevant JSONL topology as:

A  assistant tool_use: Skill
├─ B  assistant tool_use: Read              parent=A
│  └─ E  user tool_result: Read             parent=B
│     └─ ... subsequent conversation
│
└─ C  user tool_result: Skill               parent=A
   └─ D  user text: expanded Skill body     parent=C

A and B have the same message.id, confirming that they are content blocks from one batched assistant response.

During the original live turn, the next API request contains both D and E. After exiting and resuming the session, the raw API request contains:

Skill acknowledgment: present
Expanded Skill body sentinel: MISSING
Read result sentinel: present

The resumed conversation follows the E branch. D remains physically present in the JSONL but is not reconstructed into the model request.

Control Case

When Skill("example_skill") is invoked alone, the topology is linear:

A  assistant tool_use: Skill
└─ C  user tool_result: Skill
   └─ D  user text: expanded Skill body
      └─ ... subsequent conversation

After resume, the expanded Skill body sentinel remains present in the raw model request.

Deterministic Verification

I routed the resumed SDK process to a local HTTP capture endpoint instead of a real model and inspected the generated /v1/messages payload.

Using the same synthetic sentinel checks:

Control transcript:
  messages: 28
  Skill acknowledgment: present
  expanded Skill body: present
  secondary Read result: present

Parallel Skill + Read transcript:
  messages: 22
  Skill acknowledgment: present
  expanded Skill body: MISSING
  secondary Read result: present

The result repeated identically across retries. No model behavior or telemetry layer is involved in this comparison.

What Should Happen?

On resume, all content needed to reconstruct a parallel tool batch should be retained, including the synthetic user text generated by the Skill tool.

Possible fixes include:

  1. Linearizing parallel tool results and Skill expansion messages when writing the transcript, or
  2. Making resume reconstruction preserve relevant sibling branches instead of walking only one parentUuid path, or
  3. Associating the synthetic Skill body with the complete parallel tool-result message rather than a single sibling result branch.

Steps to Reproduce

  1. Start a session through the Python Claude Agent SDK.
  2. Have the model invoke Skill and another built-in tool in one parallel assistant response.
  3. Continue until at least one later tool call confirms the expanded Skill instructions are active.
  4. Exit the process and retain the JSONL session file.
  5. Resume using ClaudeAgentOptions(resume=<session_id>).
  6. Capture the outbound /v1/messages request or ask the model to follow an instruction that exists only in the Skill body.
  7. Observe that the Skill acknowledgment remains, but the expanded body is absent.
  8. Repeat with Skill invoked alone; the expanded body survives resume.

Environment

  • Python claude-agent-sdk: 0.2.128
  • Bundled Claude Code: 2.1.220
  • Platform: Linux container, aarch64
  • Resume mode: normal resume; fork_session is not enabled

The same behavior was also reproduced on an earlier SDK/CLI version pair.

Error Messages/Logs

No error is emitted. Resume succeeds silently with incomplete context.

Related Issues

  • #31330 — parallel tool results lost after resume; same parentUuid tree-versus-linear-chain mechanism
  • #37779 — sibling parallel branches dropped during session-chain reconstruction
  • #37672 — parallel calls present in JSONL but absent from resumed model context
  • #35024 — related parentUuid fork issue with a different trigger (progress entries)

This report is a Skill-specific variant: the regular Skill tool result survives, but its separately emitted expanded body is orphaned.

Privacy Note

All names, paths, identifiers, counts, and sentinel values in this report are synthetic. No production transcript or user data is attached.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗