{"record":{"id":"1a661006e611e325","repo":"vercel/ai","slug":"harness-session-this-sessionid-has-no-unfinish","errorCode":null,"errorMessage":"`Harness session ${this.sessionId} has no unfinished turn to continue.`","messagePattern":"`Harness session (.+?) has no unfinished turn to continue\\.`","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/harness/src/agent/harness-agent-session.ts","lineNumber":664,"sourceCode":"    throw new Error(\n      `Harness session ${this.sessionId} has an unfinished turn and must be continued before accepting a new prompt.`,\n    );\n  }\n\n  private requireContinuableTurn(): void {\n    if (\n      this.turnState === 'awaiting-approval' ||\n      this.turnState === 'awaiting-tool-result' ||\n      this.turnState === 'suspended'\n    ) {\n      return;\n    }\n    if (this.turnState === 'running') {\n      throw new Error(\n        `Harness session ${this.sessionId} already has a turn in progress.`,\n      );\n    }\n    throw new Error(\n      `Harness session ${this.sessionId} has no unfinished turn to continue.`,\n    );\n  }\n\n  private markAwaitingApprovalIfActive(): void {\n    if (this.sessionState === 'active') {\n      this.clearActivePromptControl();\n      this.turnState = 'awaiting-approval';\n    }\n  }\n\n  private markAwaitingToolResultIfActive(): void {\n    if (this.sessionState === 'active') {\n      this.clearActivePromptControl();\n      this.turnState = 'awaiting-tool-result';\n    }\n  }\n","sourceCodeStart":646,"sourceCodeEnd":682,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/harness/src/agent/harness-agent-session.ts#L646-L682","documentation":"Also thrown by `requireContinuableTurn()` from `continueTurn()`, for the 'idle' state: no turn exists to continue. Continuation requires a prior turn that paused or suspended; with turnState 'idle' (no turn started yet, or the previous turn already finished or failed) there is nothing to resume and the library refuses. Start a turn with `promptTurn()` first.","triggerScenarios":"Calling `continueTurn()` on a freshly created/resumed session before any promptTurn; calling continueTurn after the previous turn already completed or failed (state reset to idle by finishTrackedTurn); calling continueTurn twice after a suspension — the first continue starts a running turn, and once it finishes, a second continue finds idle.","commonSituations":"Orchestration loops that unconditionally continueTurn each iteration without checking whether the prior turn finished; retrying a failed turn with continueTurn instead of promptTurn; resuming a session from persisted state where the stored continuation was already consumed; off-by-one in slice-based schedulers that continue after the final slice.","solutions":["Call `promptTurn()` to start a turn before any continueTurn; only continue turns that are awaiting approval/tool-result or suspended.","Check `hasUnfinishedTurn()` before continueTurn and fall back to promptTurn (or exit the loop) when it is false.","After a turn failure, start a new turn with promptTurn rather than continueTurn.","Fix loop logic so continueTurn is called at most once per suspension, and stop iterating when the turn completes."],"exampleFix":"// before\n// previous turn already finished\nsession.continueTurn({ ... }); // throws: no unfinished turn\n\n// after\nif (session.hasUnfinishedTurn()) {\n  const cont = session.continueTurn({ ... });\n  await cont.ready;\n} else {\n  const turn = session.promptTurn({ prompt: 'new task', ... });\n  await turn.ready;\n}","handlingStrategy":"validation","validationCode":"if (!session.hasUnfinishedTurn()) {\n  throw new NothingToContinueError(); // start with promptTurn instead\n}","typeGuard":null,"tryCatchPattern":"try {\n  session.continueTurn({ ... });\n} catch (error) {\n  if (error instanceof Error && /has no unfinished turn to continue/.test(error.message)) {\n    session.promptTurn({ prompt, ... }); // nothing to continue; start fresh\n  } else {\n    throw error;\n  }\n}","preventionTips":["Gate continueTurn behind `hasUnfinishedTurn()`; fall back to promptTurn when idle.","Use continueTurn at most once per suspension; after completion, start a new prompt.","After a failed turn, restart with promptTurn — continueTurn cannot revive an idle session.","In slice loops, exit when the turn finishes instead of continuing unconditionally."],"tags":["lifecycle","state-machine","harness","invalid-operation"],"backgroundTag":"invalid-state-transition","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}