vercel/ai · error · Error

${harnessId} has no in-flight ACP turn to continue.

Error message

${harnessId} has no in-flight ACP turn to continue.

What it means

doContinueTurn can only resume a turn that is currently in flight. If turnInFlight is false — no turn was started, or the previous turn already completed — there is nothing to continue, so the harness throws. Continuation is meant for resuming an interrupted/incomplete turn, not for starting new work.

Source

Thrown at packages/harness-acp/src/v1/acp-v1-harness.ts:1522

        skills: options.skills,
        abortSignal: options.abortSignal,
      });
      if (options.responseFormat?.type === 'json') {
        if (options.responseFormat.schema == null) {
          throw unsupported({
            harnessId,
            message: `${harnessId} requires a JSON schema for structured output.`,
          });
        }
        if (outputSchemaMapping == null) {
          throw unsupported({
            harnessId,
            message: `${harnessId} does not support structured output through ACP.`,
          });
        }
      }
      if (!turnInFlight) {
        throw new Error(`${harnessId} has no in-flight ACP turn to continue.`);
      }
      if (lossyRerun) {
        if (latestTurnStartConfig == null || latestACPSessionId == null) {
          throw new Error(
            `${harnessId} cannot perform lossy ACP rerun without persisted start configuration and an ACP session identifier.`,
          );
        }
        assertRecoveryToolCatalog({
          persisted: latestTurnStartConfig.tools,
          current: options.tools ?? [],
        });
      }
      return wireTurn({
        emit: options.emit,
        abortSignal: options.abortSignal,
        start: () => {
          if (!lossyRerun) return;
          const turnStartConfig = latestTurnStartConfig!;

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Use promptTurn to start a new turn instead of continueTurn when no turn is in flight.
  2. Only call continueTurn when a prior turn was interrupted (e.g. after suspend/process loss) and is still logically open.
  3. Track turn state in your app (a boolean set on turn start, cleared on done/error) and call continueTurn only when it is true.
  4. If continuing after recovery, confirm the recovery path actually left a turn in flight rather than replaying it to completion.

Example fix

// before
await session.continueTurn({}); // throws when no turn is open
// after
if (turnOpen) {
  await session.continueTurn({});
} else {
  await session.promptTurn({ prompt });
}
Defensive patterns

Strategy: validation

Validate before calling

let turnOpen = false;
// set true when promptTurn starts streaming, false on done/error
if (!turnOpen) { await session.promptTurn({ prompt }); } else { await session.continueTurn({}); }

Try / catch

try {
  await session.continueTurn({});
} catch (e) {
  if (e instanceof Error && e.message.includes('no in-flight ACP turn')) {
    await session.promptTurn({ prompt: originalPrompt });
  } else throw e;
}

Prevention

When it happens

Trigger: Calling continueTurn (doContinueTurn) on a session where no promptTurn is active: before any prompt, after the previous turn finished normally, or after the session was created fresh.

Common situations: Retry logic that calls continueTurn after a turn already completed; confusing continueTurn with promptTurn for new prompts; calling continueTurn twice; calling it on a newly created session assuming a turn is pending.

Related errors


AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30). Data as JSON: /api/errors/e292fbfde2bfcd70. Report an issue: GitHub.