{"record":{"id":"983eba8520891f1e","repo":"can1357/oh-my-pi","slug":"vibe-session-record-id-no-longer-resolves-to","errorCode":null,"errorMessage":"Vibe session \"${record.id}\" no longer resolves to this parent session.","messagePattern":"Vibe session \"(.+?)\" no longer resolves to this parent session\\.","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/vibe/runtime.ts","lineNumber":945,"sourceCode":"\t\t}\n\t}\n\n\t/**\n\t * Send a message to a worker. Mid-turn and streaming → steering; mid-turn\n\t * otherwise → queued for the next turn; idle/parked → starts a new\n\t * background turn immediately.\n\t */\n\tasync send(session: ToolSession, args: { session: string; message: string }): Promise<VibeSendOutcome> {\n\t\tconst scope = this.ownerScope(session);\n\t\tconst record = this.#record(scope, args.session);\n\t\tif (record.state === \"dead\") {\n\t\t\tthrow new ToolError(`Vibe session \"${record.id}\" is dead. Spawn a new one with vibe_spawn.`);\n\t\t}\n\t\tconst message = args.message.trim();\n\t\tif (!message) throw new ToolError(\"Message must not be empty.\");\n\t\tconst registered = this.#registeredAgent(record);\n\t\tif (AgentRegistry.global().get(record.id) && !registered) {\n\t\t\tthrow new ToolError(`Vibe session \"${record.id}\" no longer resolves to this parent session.`);\n\t\t}\n\n\t\tif (record.turn) {\n\t\t\tconst live = registered?.session;\n\t\t\tif (live?.isStreaming) {\n\t\t\t\tawait live.steer(message);\n\t\t\t\trecord.lastActivityAt = Date.now();\n\t\t\t\treturn { id: record.id, mode: \"steered\" };\n\t\t\t}\n\t\t\trecord.queue.push(message);\n\t\t\trecord.lastActivityAt = Date.now();\n\t\t\treturn { id: record.id, mode: \"queued\" };\n\t\t}\n\n\t\tif (!registered || (registered.status !== \"idle\" && registered.status !== \"parked\")) {\n\t\t\tthrow new ToolError(`Vibe session \"${record.id}\" no longer resolves to this parent session.`);\n\t\t}\n","sourceCodeStart":927,"sourceCodeEnd":963,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/vibe/runtime.ts#L927-L963","documentation":"send() checks that a session id which exists in the global AgentRegistry still resolves to an agent owned by this parent session. If the id is registered globally but this registry's record has no corresponding registered agent, the session was taken over by, or re-registered under, a different owner — so this parent session may no longer message it. Thrown to prevent cross-session interference.","triggerScenarios":"Calling send() for a session id that is present in AgentRegistry.global() but #registeredAgent(record) returns undefined — i.e. the worker agent was re-registered/adopted elsewhere or the record's registration was lost while the global entry persists.","commonSituations":"Resuming a session from persisted state where another parent session re-registered the worker; duplicate ids across concurrent owner scopes; restoring a vibe record whose agent was claimed by a different run.","solutions":["Run vibe_status / list sessions from the current parent session to see which sessions it actually owns.","Spawn a new worker with vibe_spawn instead of messaging the foreign-registered id.","Avoid running multiple parent sessions that share worker names/ids concurrently.","If this is a stale record from restored state, exit Vibe mode to clean up dead/orphaned records."],"exampleFix":"// before\nawait registry.send(session, { session: adoptedId, message: 'status?' });\n// ToolError: no longer resolves to this parent session\n\n// after\nconst owned = await registry.spawn(session, { cli: 'codex', prompt: 'take over the task' });\nawait registry.send(session, { session: owned.id, message: 'status?' });","handlingStrategy":"validation","validationCode":"// Confirm the session is owned by this parent before sending\nconst owned = await registry.status(session, { sessions: [id] });\nif (!owned.length) throw new Error(`session ${id} is not owned by this parent session`);","typeGuard":null,"tryCatchPattern":"try {\n\tawait registry.send(session, { session: id, message });\n} catch (err) {\n\tif (err instanceof ToolError && err.message.includes('no longer resolves')) {\n\t\t// ownership lost: respawn a replacement worker\n\t\treturn registry.spawn(session, { cli, prompt: message });\n\t}\n\tthrow err;\n}","preventionTips":["Do not share worker sessions across multiple parent omp sessions.","Use unique worker names to avoid id collisions between scopes.","After restoring persisted state, verify ownership via vibe_status before sending.","Exit Vibe mode cleanly to drop orphaned records."],"tags":["vibe","ownership","agent-registry","session-scope"],"backgroundTag":"session-ownership-conflict","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}