vercel/ai · error · Error

${harnessId} ACP session ${sessionId} has no in-flight turn

Error message

${harnessId} ACP session ${sessionId} has no in-flight turn to suspend.

What it means

Suspending captures the channel's last seen event id and detaches from the live bridge; it requires a turn to actually be in flight. If turnInFlight is false there is nothing to suspend, so doSuspendTurn throws. To end a session with no active turn, use detach instead.

Source

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

            },
          });
        },
      });
    },
    doCompact: async () => {
      throw unsupported({
        harnessId,
        message: 'ACP v1 does not define manual session compaction.',
      });
    },
    doSuspendTurn: async () => {
      if (stopped) {
        throw new Error(
          `${harnessId} ACP session ${sessionId} is stopped; cannot suspend.`,
        );
      }
      if (!turnInFlight) {
        throw new Error(
          `${harnessId} ACP session ${sessionId} has no in-flight turn to suspend.`,
        );
      }
      stopped = true;
      const lastSeenEventId = await channel.suspend();
      return {
        type: 'continue-turn',
        harnessId,
        specificationVersion: 'harness-v1',
        data: createLifecycleData({
          bridge: {
            port: bridgePort,
            token: bridgeToken,
            lastSeenEventId,
            ...(sandboxId == null ? {} : { sandboxId }),
            stateDir: bridgeStateDir,
          },
        }),

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Call detach instead of suspendTurn when no turn is in flight.
  2. Suspend only while a turn is actively running (between prompt start and done), e.g. from the turn's event stream when an interruption is detected.
  3. Track turn state in your app and branch: suspend if in flight, detach otherwise.
  4. If the goal is just to preserve the session for later, detach with no in-flight turn returns a resume-session handle.

Example fix

// before
await session.suspendTurn(); // throws: no in-flight turn
// after
if (turnInFlight) {
  await session.suspendTurn();
} else {
  await session.detach();
}
Defensive patterns

Strategy: validation

Validate before calling

if (turnInFlight) {
  await session.suspendTurn();
} else {
  await session.detach();
}

Try / catch

try {
  await session.suspendTurn();
} catch (e) {
  if (e instanceof Error && e.message.includes('no in-flight turn to suspend')) {
    await session.detach();
  } else throw e;
}

Prevention

When it happens

Trigger: Calling suspendTurn when no turn is active: before the first prompt, after the current turn finished (done), or after a prior suspend already ended the turn.

Common situations: Calling suspend during an idle period between turns assuming it pauses the session; suspending after the turn's done event already fired; a race where the turn completed before the suspend call executed.

Related errors


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