{"record":{"id":"84f9505bd80ef542","repo":"vercel/ai","slug":"pi-session-has-been-stopped","errorCode":null,"errorMessage":"Pi session has been stopped.","messagePattern":"Pi session has been stopped\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/harness-pi/src/pi-session.ts","lineNumber":1071,"sourceCode":"    return resourcesReloaded;\n  }\n\n  /*\n   * Drive one turn against the Pi session and return the control surface.\n   * Shared by `doPromptTurn` (a fresh user prompt) and `doContinueTurn` (an empty\n   * prompt that asks Pi to continue its own thread after a rerun resume).\n   */\n  async function runTurn(turnOpts: {\n    text: string;\n    model?: string;\n    skills: ReadonlyArray<HarnessV1Skill>;\n    tools: ReadonlyArray<HarnessV1ToolSpec>;\n    instructions?: string;\n    emit: (part: HarnessV1StreamPart) => void;\n    abortSignal?: AbortSignal;\n  }): Promise<HarnessV1PromptControl> {\n    if (stopped) {\n      throw new Error('Pi session has been stopped.');\n    }\n\n    const skillWriteResult = await writePiSkills({\n      sandbox: toolSafeSandboxSession,\n      sandboxHomeDir,\n      skills: turnOpts.skills,\n      abortSignal: turnOpts.abortSignal,\n    });\n    harnessSkills = createHarnessPiSkills({\n      skills: turnOpts.skills,\n      sandboxSkillRootDir,\n    });\n    if (piSession != null && skillWriteResult.changed) {\n      await reloadResourcesOnly();\n    }\n\n    const userTools = turnOpts.tools;\n    currentEmit = turnOpts.emit;","sourceCodeStart":1053,"sourceCodeEnd":1089,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/harness-pi/src/pi-session.ts#L1053-L1089","documentation":"Once a Pi session has been stopped via stop(), any new prompt turn is rejected because the adapter no longer accepts work for a terminated session. The guard at the top of the prompt-turn path throws immediately when `stopped` is true.","triggerScenarios":"Calling prompt/doPromptTurn after a previous call to `stop()` on the same session handle, or on a handle whose session was stopped in a prior lifecycle step.","commonSituations":"Reusing a stale session object after an intentional stop; a UI that keeps a reference after the user clicked 'stop'; error-retry logic retrying prompts on a stopped session.","solutions":["Create a new session (createPi) or resume from persisted state instead of prompting the stopped handle","Track stop state in your app before issuing further prompts","If the stop was unintentional, avoid calling stop() until the run is complete"],"exampleFix":"// before\nawait session.stop();\nawait session.prompt({ prompt: 'one more' }); // throws: stopped\n// after\nawait session.stop();\nconst session2 = await pi.resumeSession({ ...state });\nawait session2.prompt({ prompt: 'one more' });","handlingStrategy":"type-guard","validationCode":"// track stop state yourself\nlet stopped = false;\nconst guard = () => { if (stopped) throw new Error('session stopped locally'); };","typeGuard":"function isStopped(err: unknown): boolean {\n  return err instanceof Error && err.message === 'Pi session has been stopped.';\n}","tryCatchPattern":"try {\n  await session.prompt(opts);\n} catch (e) {\n  if (isStopped(e)) {\n    const fresh = await pi.resumeSession({ ...savedState });\n    return fresh.prompt(opts);\n  }\n  throw e;\n}","preventionTips":["Disable prompt inputs after stop() in the UI","Null out or mark stale session handles once stop resolves","Prefer resume-from-state over reusing a stopped handle"],"tags":["lifecycle","stopped-session"],"backgroundTag":"session-already-stopped","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}