vercel/ai · error · Error
${harnessId} ACP session ${sessionId} has an in-flight turn;
Error message
${harnessId} ACP session ${sessionId} has an in-flight turn; suspend it instead. What it means
Detach requires an idle session. If a turn is currently in flight, doDetach refuses to tear down the attachment because the in-flight turn would be lost; the caller must suspend instead, which captures the turn state and returns a continue-turn handle for later resumption.
Source
Thrown at packages/harness-acp/src/v1/acp-v1-harness.ts:1621
data: createLifecycleData({
bridge: {
port: bridgePort,
token: bridgeToken,
lastSeenEventId,
...(sandboxId == null ? {} : { sandboxId }),
stateDir: bridgeStateDir,
},
}),
};
},
doDetach: async () => {
if (stopped) {
throw new Error(
`${harnessId} ACP session ${sessionId} is stopped; cannot detach.`,
);
}
if (turnInFlight) {
throw new Error(
`${harnessId} ACP session ${sessionId} has an in-flight turn; suspend it instead.`,
);
}
stopped = true;
const lastSeenEventId = await channel.suspend();
return {
type: 'resume-session',
harnessId,
specificationVersion: 'harness-v1',
data: createLifecycleData({
bridge: {
port: bridgePort,
token: bridgeToken,
lastSeenEventId,
...(sandboxId == null ? {} : { sandboxId }),
stateDir: bridgeStateDir,
},
}),View on GitHub (pinned to 69428b1f8b)
Solutions
- Call suspendTurn instead of detach while a turn is in flight; it returns a continue-turn handle to resume the turn later.
- Wait for the current turn to finish (done event) before detaching.
- Abort the in-flight turn first, then detach once turnInFlight is false.
- If migrating mid-turn is the goal, design around suspend + resume rather than detach.
Example fix
// before await session.detach(); // throws: turn in flight // after const handle = await session.suspendTurn(); // continue-turn handle // later: resume and continue the turn
Defensive patterns
Strategy: validation
Validate before calling
if (turnInFlight) {
const handle = await session.suspendTurn(); // continue-turn handle
} else {
const handle = await session.detach(); // resume-session handle
} Try / catch
try {
await session.detach();
} catch (e) {
if (e instanceof Error && e.message.includes('in-flight turn; suspend it instead')) {
await session.suspendTurn();
} else throw e;
} Prevention
- Never detach during an active turn; wait for done or abort first
- Route mid-turn shutdown through suspendTurn
- Defer cleanup callbacks until the turn stream completes
When it happens
Trigger: Calling detach while a promptTurn is actively streaming (turnInFlight=true) — e.g. attempting to detach mid-turn to migrate the session, or a cleanup routine firing during an active turn.
Common situations: App shutdown while a long turn is still running; trying to hand the session to another process mid-turn via detach; a timeout handler detaching while the model is still generating.
Related errors
- ${harnessId} cannot start a new ACP prompt while a turn is i
- ${harnessId} ACP session ${sessionId} is stopped; cannot sus
- ${harnessId} ACP session ${sessionId} has no in-flight turn
- ${harnessId} ACP session ${sessionId} is stopped; cannot det
- The ${model.provider} model "${model.modelId}" does not supp
AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30).
Data as JSON: /api/errors/d9bd8c3a31b59c85.
Report an issue: GitHub.