vercel/ai · error
claude-code session ${sessionId} is stopped; cannot suspend.
Error message
claude-code session ${sessionId} is stopped; cannot suspend. What it means
doSuspendTurn freezes the host at a precise event cursor so the turn can later be resumed. Because suspension also marks the session stopped, calling it on a session whose `stopped` flag is already set (from a prior suspend, stop, or detach) throws this error — there is no live channel cursor left to freeze.
Source
Thrown at packages/harness-claude-code/src/claude-code-harness.ts:2046
specificationVersion: 'harness-v1',
// The bridge's stop reply carries the session id it observed; the
// adapter's own record backfills it when the reply predates the
// field or the channel was already closed.
data: {
...(lastClaudeSessionId
? { claudeSessionId: lastClaudeSessionId }
: {}),
...lifecycleData,
...(sandboxCredentialEnvironment == null
? {}
: { sandboxCredentialEnvironment }),
} as HarnessV1ResumeSessionState['data'],
};
return payload;
},
doSuspendTurn: async () => {
if (stopped) {
throw new Error(
`claude-code session ${sessionId} is stopped; cannot suspend.`,
);
}
stopped = true;
/*
* Freeze the host at a precise cursor without stopping the active model
* turn. `channel.suspend` stops processing inbound frames, drains what
* was already dispatched, then closes the host socket with reason
* `'suspended'`. The bridge keeps the turn running and accumulates events
* past the cursor for the next slice to replay. The sandbox process is
* deliberately left alive.
*/
const lastSeenEventId = await channel.suspend();
const payload: HarnessV1ContinueTurnState = {
type: 'continue-turn',
harnessId: 'claude-code',
specificationVersion: 'harness-v1',
data: {View on GitHub (pinned to 69428b1f8b)
Solutions
- Only call doSuspendTurn once per session lifecycle; track suspension with a local flag or promise
- Catch this error and treat it as success if your goal was already achieved by the earlier suspend/stop
- If the session was suspended and resumed, operate on the new session handle returned by the resume flow, not the original one
- If you intended to end the session rather than pause it, call doStop instead
Example fix
// before
await session.doSuspendTurn();
await session.doSuspendTurn(); // throws: stopped; cannot suspend
// after
let suspended = false;
async function suspendOnce(session) {
if (suspended) return;
suspended = true;
return session.doSuspendTurn();
} Defensive patterns
Strategy: try-catch
Validate before calling
const suspendedSessions = new Set<string>();
function canSuspend(sessionId: string): boolean {
return !suspendedSessions.has(sessionId);
} Type guard
function isStoppedSessionError(err: unknown): boolean {
return err instanceof Error && err.message.includes('is stopped; cannot suspend');
} Try / catch
try {
await session.doSuspendTurn();
} catch (err) {
if (!isStoppedSessionError(err)) throw err;
// already suspended/stopped: nothing to do
} Prevention
- Suspend exactly once per session: guard with a promise or flag
- After resuming, use the new session handle; never re-suspend with the pre-resume handle
- Choose deliberately between doStop (terminate) and doSuspendTurn (pause) — they both set stopped
- Add shutdown hooks that check a suspended-set before suspending again
When it happens
Trigger: Calling session.doSuspendTurn() after the session was already stopped via a previous doSuspendTurn, doStop, or doDetach; or after the session was suspended and its state handed off for resume.
Common situations: An app that suspends on both a shutdown hook and an explicit pause action; retrying a suspend after a failure elsewhere in the shutdown path; attempting to suspend a session that was restored from a previous suspend operation (the old handle remains stopped — use the resumed session).
Related errors
- claude-code session ${sessionId} is already stopped; cannot
- claude-code session ${sessionId} is already stopped; cannot
- ${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
AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30).
Data as JSON: /api/errors/218c3749701cec1d.
Report an issue: GitHub.