paperclipai/paperclip · error
OpenCode tool call is not bound to an active turn
Error message
OpenCode tool call is not bound to an active turn
What it means
`dispatchTool` routes each OpenCode MCP tool call into the Paperclip event stream tagged with the currently active turn. If `#activeTurnId` is null — no turn is running — the tool call cannot be attributed to a turn and is rejected. This happens when OpenCode emits tool calls outside an active prompt window (e.g. late replays after the turn ended).
Source
Thrown at packages/paperclip-runner/src/drivers/opencode/opencode-server-driver.ts:875
? harnessRuntimeRequestOutcome(request, { reason: "session_closed" })
: harnessRuntimeInputExpiredOutcome(request, "provider_process_lost"),
{ turnId: request.turnId, itemId: request.itemId },
);
}
this.#pendingRuntimeRequests.clear();
this.#events.close();
await this.#runtime.close();
}
async dispatchTool(call: {
tool: string;
callId: string;
arguments: unknown;
}): Promise<unknown> {
const tool = canonicalOpenCodeMcpToolName(call.tool);
const turnId = this.#activeTurnId;
if (turnId === null)
throw new Error("OpenCode tool call is not bound to an active turn");
this.#emit(
"item.started",
{
kind: "dynamicToolCall",
item: {
type: "tool_call",
id: call.callId,
name: tool,
arguments: call.arguments,
},
},
{ turnId, itemId: call.callId },
);
if (tool === PRP_COMPLETION_TOOL_NAME || tool === PRP_BLOCK_TOOL_NAME) {
const validation = validatePrpStructuredRunResult(call.arguments);
if (!validation.ok) throw new Error("Invalid semantic result");
if (
(tool === PRP_BLOCK_TOOL_NAME &&View on GitHub (pinned to 01ad858492)
Solutions
- Only call `dispatchTool` between a successful `startTurn` and the turn's terminal event; check `await session.snapshot()` (`activeTurnId !== null`) first.
- Filter the OpenCode event stream by source sequence / turn so stale tool calls are dropped instead of dispatched.
- If this occurs during reconnect replay, skip the event rather than dispatch — the turn's results were already finalized.
- Start a turn (`startTurn`) before issuing tool calls if your integration drives tools proactively.
Example fix
// before
await session.dispatchTool({ tool, callId, arguments }); // throws when no active turn
// after
const snap = await session.snapshot();
if (snap.activeTurnId) {
await session.dispatchTool({ tool, callId, arguments });
} Defensive patterns
Strategy: validation
Validate before calling
const snap = await session.snapshot(); if (!snap.activeTurnId) return; // drop out-of-turn tool call
Type guard
function hasActiveTurn(snap) { return typeof snap.activeTurnId === 'string' && snap.activeTurnId.length > 0; } Try / catch
try {
await session.dispatchTool({ tool, callId, arguments });
} catch (e) {
if (e.message === 'OpenCode tool call is not bound to an active turn') {
logger.warn({ callId }, 'dropped stale tool call outside active turn');
} else throw e;
} Prevention
- Dispatch tools only between startTurn and the turn's terminal event.
- Filter replayed SSE events by source sequence on reconnect.
- Check `snapshot().activeTurnId` before dispatch.
- Start a turn before proactively invoking tools.
When it happens
Trigger: Calling `session.dispatchTool({ tool, callId, arguments })` before any `startTurn`, after the active turn completed/aborted (turn id cleared), or after a resume where `attachRun` was called but no new turn started while OpenCode still replays buffered tool calls.
Common situations: Replaying SSE history after reconnect: old tool-call events arrive when the driver considers the turn done; a client invokes driver-level tool dispatch directly outside the prompt loop; race where `turn.completed` clears state just before a final tool call lands.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- OpenCode evals require exact version 1.18.17; received ${ver
- [opencode-local] Remote model availability probe for "${mode
- [opencode-local] Remote `opencode models` returned no models
- OpenCode ${QUALIFIED_OPENCODE_VERSION} runner-owned executab
- [opencode-local] Model availability probe could not run for
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/08494b15821fe4f7.
Report an issue: GitHub.