{"record":{"id":"5ae13960a8a59466","repo":"vercel/ai","slug":"pi-has-no-active-runtime-session-to-steer","errorCode":null,"errorMessage":"Pi has no active runtime session to steer.","messagePattern":"Pi has no active runtime session to steer\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/harness-pi/src/pi-session.ts","lineNumber":896,"sourceCode":"         * (so the model reads it), and Pi echoes that text back — without this\n         * the consumer-facing result would be the serialized string instead of\n         * the original object.\n         */\n        translatorState?.hostToolResults.set(args.toolCallId, args.output);\n        pending.resolve(args.output);\n      },\n      async submitToolApproval(args) {\n        const pending = pendingToolApprovals.get(args.approvalId);\n        if (!pending) return;\n        pendingToolApprovals.delete(args.approvalId);\n        pending.resolve({\n          approved: args.approved,\n          reason: args.reason,\n        });\n      },\n      async submitUserMessage(text) {\n        if (piSession == null) {\n          throw new Error('Pi has no active runtime session to steer.');\n        }\n        await piSession.steer(text);\n      },\n      done: input.done,\n    };\n  }\n\n  async function requestBuiltinToolApproval(args: {\n    toolCallId: string;\n    nativeName: (typeof PI_NATIVE_BUILTIN_NAMES)[number];\n  }): Promise<{ approved: boolean; reason?: string }> {\n    if (\n      !piBuiltinToolRequiresApproval({\n        permissionMode,\n        kind: PI_NATIVE_TOOL_KINDS[args.nativeName],\n      })\n    ) {\n      return { approved: true };","sourceCodeStart":878,"sourceCodeEnd":914,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/harness-pi/src/pi-session.ts#L878-L914","documentation":"submitUserMessage on the returned prompt control requires a live Pi runtime session (`piSession`). The adapter throws this error when steering is requested before any Pi session has been created/activated. It is a lifecycle-state guard, not a network failure.","triggerScenarios":"Calling `submitUserMessage` (steer) on a session/resumedSession handle before the first turn has started a Pi runtime session, or after the underlying Pi session was torn down without being re-established.","commonSituations":"Steering a freshly created Pi session before calling prompt; racing steer against session teardown; resuming a session whose Pi runtime was never started in this process.","solutions":["Start a turn (prompt/continue) so the Pi runtime session is active before steering","Check session lifecycle state before calling submitUserMessage","Re-create or re-resume the session if the runtime was stopped"],"exampleFix":"// before\nconst ctrl = await session.submitUserMessage('go left'); // no active turn yet\n// after\nawait session.prompt({ prompt: 'start' }); // establishes Pi session\nconst ctrl = await session.submitUserMessage('go left');","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await session.submitUserMessage(text);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('no active runtime session')) {\n    await session.prompt({ prompt: text }); // start a turn instead of steering\n  } else {\n    throw e;\n  }\n}","preventionTips":["Only offer steering controls once a turn is active in the UI","Ensure the resume path actually starts a Pi runtime session before exposing steer","Track 'active Pi session' state alongside your session handle"],"tags":["lifecycle","steering","session-state"],"backgroundTag":"no-active-session","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}