Skip to content

fix(tabs): forking into a tab no longer picks a turn that is still running - #277

Merged
adampeterhiggins merged 1 commit into
mainfrom
ah/investigate-fork-error
Oct 12, 2026
Merged

adampeterhiggins merged 1 commit into
mainfrom
ah/investigate-fork-error

Conversation

@adampeterhiggins

Copy link
Copy Markdown
Owner

Continuing a sibling tab from an empty tab's context pill failed with "Could not fork into a new tab — The environment failed to answer this request (internal_error)" while that sibling was mid-turn. The server picked the latest completed assistant message as the fork point, but a running Claude turn completes messages between its tool calls, so the point landed on the in-progress run. The orchestrator then refused it (Fork source run … is running; in-progress and rolled-back runs cannot be forked), and the tab route turned that dispatch failure into a 500.

Fix

  • latestThreadForkPoint now takes the thread's runs and skips responses whose run has not finished, using the orchestrator's own isForkableSourceRunStatus rule (moved into contracts so both share it). Continuing from a busy tab now forks from its last finished turn.
  • All callers pass runs: the server's continue/agent-open path, and the web and mobile "fork onto another model" and "fork from before a message" paths.
  • ThreadTabs.forkTab refuses an explicit fork of the source's active run as invalid_request with a readable detail, rather than letting it fail as an internal dispatch error. Agents opening tabs over MCP get that message.

The per-response fork button already hides during a running turn on web and mobile, so it needed no change.

Tests: vp test run packages/client-runtime/src/threadTabs.test.ts apps/server/src/threadTabs/ThreadTabs.test.ts apps/server/src/orchestration-v2/ThreadForkService.test.ts (26 passed). Typecheck clean for contracts, client-runtime, server, web, mobile.

Done by Claude Opus 5.5 in Claude Code (via T3 Code).

🤖 Generated with Claude Code

…nning

Continuing a sibling tab, forking onto another model, or forking from before
a message chose the latest completed assistant message. A running Claude turn
completes messages between its tool calls, so the fork could point at the
in-progress run, which the orchestrator rejects and the tab route reported as
internal_error.

The fork point now skips responses of runs that have not finished, using the
same forkable-status rule as the orchestrator (moved to contracts). The tab
service also refuses an explicit fork of the source's active run as an
invalid request instead of a dispatch failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant