can1357/oh-my-pi · error · ToolError
Vibe session "${record.id}" is dead. Spawn a new one with vi
Error message
Vibe session "${record.id}" is dead. Spawn a new one with vibe_spawn. What it means
VibeSessionRegistry.send() refuses to deliver a message to a vibe worker whose record is in the 'dead' state. A dead record means the worker spawned but terminally failed (crashed, was killed, or its spawn/turn failed permanently) and can no longer accept messages. The error explicitly directs the caller to spawn a replacement worker with vibe_spawn.
Source
Thrown at packages/coding-agent/src/vibe/runtime.ts:939
if (!record.terminalPersisted) {
throw new ToolError("Vibe parent session changed before spawn failure could be persisted.");
}
}
this.#records.delete(key);
throw error;
}
}
/**
* Send a message to a worker. Mid-turn and streaming → steering; mid-turn
* otherwise → queued for the next turn; idle/parked → starts a new
* background turn immediately.
*/
async send(session: ToolSession, args: { session: string; message: string }): Promise<VibeSendOutcome> {
const scope = this.ownerScope(session);
const record = this.#record(scope, args.session);
if (record.state === "dead") {
throw new ToolError(`Vibe session "${record.id}" is dead. Spawn a new one with vibe_spawn.`);
}
const message = args.message.trim();
if (!message) throw new ToolError("Message must not be empty.");
const registered = this.#registeredAgent(record);
if (AgentRegistry.global().get(record.id) && !registered) {
throw new ToolError(`Vibe session "${record.id}" no longer resolves to this parent session.`);
}
if (record.turn) {
const live = registered?.session;
if (live?.isStreaming) {
await live.steer(message);
record.lastActivityAt = Date.now();
return { id: record.id, mode: "steered" };
}
record.queue.push(message);
record.lastActivityAt = Date.now();
return { id: record.id, mode: "queued" };View on GitHub (pinned to 9690622007)
Solutions
- Spawn a new worker with vibe_spawn (optionally passing the same name to get a successor id) and resend the message to the new session.
- Check the session's lastActivity (e.g. 'spawn failed' / kill reason) via vibe_status to understand why it died before respawning.
- If the death was caused by a bad prompt or model config, fix the prompt/cli/model args before respawning.
- Update orchestration code to handle dead sessions by respawning instead of retrying send.
Example fix
// before
await registry.send(session, { session: deadId, message: 'continue' });
// ToolError: Vibe session "x" is dead.
// after
const spawned = await registry.spawn(session, { cli: 'codex', prompt: 'continue the work' });
await registry.send(session, { session: spawned.id, message: 'continue' }); Defensive patterns
Strategy: validation
Validate before calling
// Look up the record's state before sending
const status = await registry.status(session, { sessions: [id] });
if (status.find(s => s.id === id)?.state === 'dead') {
// respawn instead of sending
} Type guard
function isAliveRecord(record: VibeRecord): boolean {
return record.state !== 'dead';
} Try / catch
try {
await registry.send(session, { session: id, message });
} catch (err) {
if (err instanceof ToolError && err.message.includes('is dead')) {
const spawned = await registry.spawn(session, { cli, prompt: message });
return spawned;
}
throw err;
} Prevention
- Check vibe_status before messaging a worker after any failure.
- Treat worker ids as single-task: never reuse an id across tasks.
- Build respawn-on-death logic into orchestrators instead of retrying send.
- Inspect lastActivity to learn why the worker died before respawning.
When it happens
Trigger: Calling send(session, { session: '<id>', message }) with the id of a vibe session whose record.state === 'dead' — e.g. after the worker process crashed, was killed via vibe_kill, or a previous turn failed terminally.
Common situations: Agent workflow sends messages to a worker that crashed earlier in the run; reusing a stored session id from a previous task after the worker died; a worker killed due to error/timeout then addressed again by a queued follow-up message.
Related errors
- Vibe parent session changed before spawn failure could be pe
- Message must not be empty.
- Vibe session "${record.id}" no longer resolves to this paren
- Vibe tombstone recovery requires parent-session persistence.
- Vibe session "${record.id}" changed parent scope before its
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/0ea06e30174c06c5.
Report an issue: GitHub.