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

  1. Resolve the pending tool approval (approve/reject via the session's toolApproval flow) so the turn can finish, then prompt anew.
  2. Supply the pending tool result the turn is waiting for, letting it complete before the next prompt.
  3. Call `continueTurn()` to resume/finish the awaiting or suspended turn instead of starting a fresh prompt.
  4. If the paused turn is no longer wanted, stop/destroy the session and create a new session for the new prompt.
  5. 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

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


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