vercel/ai · error
`Harness session ${this.sessionId} has an unfinished turn an
Error message
`Harness session ${this.sessionId} has an unfinished turn and must be continued before accepting a new prompt.` What it means
Also thrown by `requirePromptableTurn()` from `promptTurn()`, for turn states that are neither 'idle' nor 'running' — namely 'awaiting-approval', 'awaiting-tool-result', or 'suspended'. The session has an unfinished, paused turn (waiting on a tool approval, a tool result, or held as a suspended continuation), and a brand-new prompt is not allowed; the caller must continue, approve, or resume the existing turn instead.
Source
Thrown at packages/harness/src/agent/harness-agent-session.ts:646
}): HarnessAgentResumeSessionState {
const { continueFrom } = options;
return {
type: 'resume-session',
harnessId: continueFrom.harnessId,
specificationVersion: continueFrom.specificationVersion,
data: continueFrom.data,
continueFrom,
};
}
private requirePromptableTurn(): void {
if (this.turnState === 'idle') return;
if (this.turnState === 'running') {
throw new Error(
`Harness session ${this.sessionId} already has a turn in progress.`,
);
}
throw new Error(
`Harness session ${this.sessionId} has an unfinished turn and must be continued before accepting a new prompt.`,
);
}
private requireContinuableTurn(): void {
if (
this.turnState === 'awaiting-approval' ||
this.turnState === 'awaiting-tool-result' ||
this.turnState === 'suspended'
) {
return;
}
if (this.turnState === 'running') {
throw new Error(
`Harness session ${this.sessionId} already has a turn in progress.`,
);
}
throw new Error(View on GitHub (pinned to 69428b1f8b)
Solutions
- Resolve the pending tool approval (approve/reject via the session's toolApproval flow) so the turn can finish, then prompt anew.
- Supply the pending tool result the turn is waiting for, letting it complete before the next prompt.
- Call `continueTurn()` to resume/finish the awaiting or suspended turn instead of starting a fresh prompt.
- If the paused turn is no longer wanted, stop/destroy the session and create a new session for the new prompt.
- Check `hasUnfinishedTurn()` and branch: continue the unfinished turn rather than prompting.
Example fix
// before
// turn is suspended at a stop-condition boundary
session.promptTurn({ prompt: 'next task', ... }); // throws: unfinished turn
// after
const cont = session.continueTurn({ ... }); // resume the unfinished turn
await cont.ready;
await cont.result;
session.promptTurn({ prompt: 'next task', ... }); // now allowed (turnState idle) Defensive patterns
Strategy: validation
Validate before calling
if (session.hasUnfinishedTurn()) {
// finish the paused turn: resolve approvals/tool results or continueTurn()
throw new UnfinishedTurnError();
} Try / catch
try {
session.promptTurn({ prompt, ... });
} catch (error) {
if (error instanceof Error && /must be continued before accepting a new prompt/.test(error.message)) {
const cont = session.continueTurn({ ... }); // resume paused/suspended turn
await cont.ready;
session.promptTurn({ prompt, ... });
} else {
throw error;
}
} Prevention
- Wire up the tool-approval and tool-result flows so turns never stall in awaiting states.
- After suspension at a slice boundary, resume with continueTurn — never a new prompt.
- Check `hasUnfinishedTurn()` and branch to continue vs. prompt.
- Stop/destroy and recreate the session if a paused turn is no longer wanted.
When it happens
Trigger: Calling `promptTurn()` while the session is (a) waiting for a tool approval decision (markAwaitingApprovalIfActive set 'awaiting-approval'), (b) waiting for a pending tool result to be supplied ('awaiting-tool-result'), or (c) suspended at a stop-condition boundary awaiting `continueTurn()`.
Common situations: Sending a fresh user message while the agent is blocked on a tool-permission prompt; dropping the approval/tool-result flow and trying to 'restart' with a new prompt; attempting a new prompt after suspending at a slice boundary instead of resuming with continueTurn; state machines that lose track of pending approvals.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Harness session ${this.sessionId} has no unfinished turn to
- `Harness session ${this.sessionId} already has a turn in pro
- `Harness session ${this.sessionId} has no unfinished turn to
- `Harness '${input.harness.harnessId}' emitted a built-in too
- `Harness '${input.harness.harnessId}' emitted approval reque
AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30).
Data as JSON: /api/errors/e224caf8ee044053.
Report an issue: GitHub.