github / github/copilot-cli

Bug: Concurrent sub-agent events corrupt session state — permanent "tool_use ids were found without tool_result blocks" error

Open
#2,543 4 comments 2 reactions 0 assignees View on GitHub
area:agents area:sessions
Dominant language
Shell
Stars
11.2k
Forks
1.9k
Avg merge
14h 16m
Merged PRs (30d)
6

Description

## Error

```
CAPIError: 400 messages.N: `tool_use` ids were found without `tool_result` blocks immediately after: toolu_XXXX.
Each `tool_use` block must have a corresponding `tool_result` block in the next message.
```

This error occurs on **every subsequent message** after corruption -- the session is permanently bricked with no built-in recovery mechanism.

## Environment

- **Copilot CLI version:** 1.0.19
- **OS:** Windows 11
- **Model:** Claude Opus 4.6 (1M context)

## Root Cause

When the main conversation launches a sub-agent (via the `task` tool -- e.g., explore, rubber-duck, or general-purpose agents) and continues making tool calls while the sub-agent runs, both conversations write events to the same `events.jsonl` file. The runtime's message reconstruction does not properly separate the two conversations' tool calls when building the API request. The Claude API then rejects the request because `tool_use` blocks appear without matching `tool_result` blocks in the next message.

## Steps to Reproduce

1. Use a **long-lived session** (one that has undergone at least one compaction)
2. Trigger a `task` tool call to launch a **background sub-agent** (explore, rubber-duck, etc.)
3. While the sub-agent runs, **continue interacting** with the main conversation -- for example, `read_agent` with `wait: true` returns a timeout, and then the assistant makes additional tool calls
4. Sub-agent events (`assistant.message`, `tool.execution_start`, `tool.execution_complete`) interleave with main conversation events in `events.jsonl`
5. **Every subsequent message permanently fails** with the `tool_use`/`tool_result` mismatch error

### Key observation

In the corrupted session we analyzed:
- A sub-agent that ran with **no** main conversation events during its execution -- **no corruption**
- A sub-agent that ran with **110+ main conversation events interleaved** -- **19 orphaned tool calls, session permanently broken**

The difference is whether the main conversation was actively producing events while the sub-agent was running.

## Workaround / Repair Steps

There is no built-in recovery. We manually repaired the session by editing `events.jsonl`:

### Diagnosis

1. Opened `~/.copilot/session-state//events.jsonl`
2. Found the last `session.compaction_complete` event (this is where the runtime starts reconstructing the conversation)
3. Indexed all `assistant.message` events with `toolRequests` after the compaction, along with their `tool.execution_start` and `tool.execution_complete` events
4. For each tool call, checked if the gap between `execution_start` and `execution_complete` contained `assistant.message` or `assistant.turn_start` events from a **different `interactionId`** -- this indicates the sub-agent's events were interleaved

### Repair

1. **Backed up** `events.jsonl` to `events.jsonl.bak`
2. **Removed all events** (any type) that referenced any of the problematic tool call IDs -- this covers the main conversation's tool_use, the sub-agent's internal events, hooks, error events, etc.
3. **Cascaded removal** via `parentId` chains -- removed orphaned `hook.start`/`hook.end` and `tool.execution_start`/`tool.execution_complete` events whose parent was removed
4. **Swept for collateral orphans** -- after removal, some tool calls lost their `tool.execution_complete` as collateral damage, leaving a new `tool_use` without a `tool_result`. Iteratively detected and removed these until no orphans remained
5. **Fixed parent chain links** -- updated `parentId` references on surviving `assistant.turn_end` events to point to the nearest surviving ancestor
6. **Removed all `session.error` events** (just repeated failure logs from retries)

A reusable PowerShell repair script is available in [this repair script](https://gist.github.com/KeithIsSleeping/1a919a69a5707b764d880cf9e38e5e70).

## Suggested Fix

The runtime's message reconstruction should filter events by conversation/interaction context before building the Claude API messages array. Sub-agent internal events (`assistant.message`, `tool.execution_start/complete`, `assistant.turn_start/end`) should not be included in the main conversation's messages -- they belong to a separate conversation context.

Alternatively, sub-agent events could be written to a separate events file (e.g., `events..jsonl`) to prevent interleaving entirely.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.