{"record":{"id":"95c21ac1746667b2","repo":"vercel/ai","slug":"no-acp-prompt-turn-is-active","errorCode":null,"errorMessage":"No ACP prompt turn is active.","messagePattern":"No ACP prompt turn is active\\.","errorType":"http","errorClass":"RelayRequestError","httpStatus":409,"severity":"error","filePath":"packages/harness-acp/src/v1/bridge/host-tool-relay.ts","lineNumber":272,"sourceCode":"async function handleInvocation({\n  body,\n  state,\n  serverName,\n  turn,\n  nextInvocationOrder,\n}: {\n  body: unknown;\n  state: CatalogState;\n  serverName: string;\n  turn: HostToolRelayTurn | undefined;\n  nextInvocationOrder: () => number;\n}): Promise<{\n  output: unknown;\n  isError?: boolean;\n  correlationToken: string;\n}> {\n  if (turn == null) {\n    throw new RelayRequestError({\n      status: 409,\n      message: 'No ACP prompt turn is active.',\n    });\n  }\n  if (\n    !isRecord(body) ||\n    typeof body.requestId !== 'string' ||\n    typeof body.toolName !== 'string' ||\n    !isRecord(body.input) ||\n    !Number.isSafeInteger(body.catalogRevision)\n  ) {\n    throw new RelayRequestError({\n      status: 400,\n      message: 'Invalid host tool relay request.',\n    });\n  }\n  if (body.catalogRevision !== state.revision) {\n    throw new RelayRequestError({","sourceCodeStart":254,"sourceCodeEnd":290,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/harness-acp/src/v1/bridge/host-tool-relay.ts#L254-L290","documentation":"Invoking a host tool through /invoke requires that an ACP prompt turn is currently bound to the relay (activeTurn set via bindTurn). If no turn is bound, the relay cannot emit tool calls or await tool results, so handleInvocation rejects with this 409 RelayRequestError before reading further body validation.","triggerScenarios":"POSTing /invoke between turns (after unbindTurn, before the next bindTurn), invoking during session startup before any prompt turn started, or after a turn ended/aborted and unbound while the MCP client still had a pending tool call.","commonSituations":"MCP client with a long-lived connection firing a tool call after the ACP turn finished; async background work in the agent deferring a tool invocation past turn end; a crashed turn never rebound; testing /invoke directly without a running prompt turn.","solutions":["Only invoke host tools while an ACP prompt turn is active; re-issue the call from within the turn.","Bind the turn before serving /invoke traffic: relay.bindTurn({ turn }) at turn start.","Make the host-side client wait/retry until a turn becomes active, or return a clear error to the model to retry inside a prompt.","Check that a prior turn's error path did not leave the relay unbound when you expected it bound."],"exampleFix":"// before\nawait invokeTool(toolName, input); // fires any time\n// after\nif (!isTurnActive) {\n  throw new Error('Host tool invoked outside an ACP prompt turn; retry within a turn.');\n}\nawait invokeTool(toolName, input);","handlingStrategy":"try-catch","validationCode":"// Caller-side check before invoking a host tool\nfunction assertTurnActive(turn: HostToolRelayTurn | undefined) {\n  if (turn == null) {\n    throw new Error('Host tools can only be invoked during an active ACP prompt turn.');\n  }\n}","typeGuard":null,"tryCatchPattern":"const res = await postJson(invokeUrl, body);\nif (res.status === 409 && (await res.json()).error === 'No ACP prompt turn is active.') {\n  // defer the tool call until the next turn, or surface a retryable error to the agent\n}","preventionTips":["Fire host tool calls only from within the prompt-turn lifecycle.","Bind the turn before any MCP client can reach /invoke.","Cancel or queue background work that outlives the turn instead of letting it invoke tools.","In tests, always bind a turn before exercising /invoke."],"tags":["relay","state-conflict","lifecycle","harness-acp"],"backgroundTag":"no-active-session-turn","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}