can1357/oh-my-pi · error · ToolError
Vibe mode exit cannot persist worker tombstones without the
Error message
Vibe mode exit cannot persist worker tombstones without the parent session manager.
What it means
When vibe mode exits, worker tombstones must be appended to the parent session so records survive restarts. #persistModeExit throws if any pending record has a childSessionFile but session.sessionManager is null — persistence is impossible. Records without a child session file are silently marked terminal-persisted instead.
Source
Thrown at packages/coding-agent/src/vibe/runtime.ts:490
) {
continue;
}
if (event.action === "tombstone") terminalReason = event.reason;
else if (event.action === "tombstone-revoked" && terminalReason === "mode-exit") terminalReason = undefined;
}
return terminalReason !== undefined;
}
async #persistModeExit(
session: VibeParentSession,
scope: VibeOwnerScope,
records: readonly VibeRecord[],
): Promise<void> {
const pending = records.filter(record => !record.terminalPersisted);
const sessionManager = session.sessionManager;
if (!sessionManager) {
if (pending.some(record => record.childSessionFile)) {
throw new ToolError("Vibe mode exit cannot persist worker tombstones without the parent session manager.");
}
for (const record of pending) record.terminalPersisted = true;
return;
}
const currentScope = this.ownerScope(session);
if (
currentScope.ownerId !== scope.ownerId ||
currentScope.parentSessionId !== scope.parentSessionId ||
currentScope.parentSessionFile !== scope.parentSessionFile
) {
throw new ToolError("Vibe parent session changed before mode exit could be persisted.");
}
const parentSessionFile = currentScope.parentSessionFile;
const persistedPending = pending.filter(record => record.childSessionFile !== undefined);
for (const record of persistedPending) {
if (
!parentSessionFile ||
path.resolve(parentSessionFile.slice(0, -6), `${record.id}.jsonl`) !== record.childSessionFileView on GitHub (pinned to 9690622007)
Solutions
- Attach a real session manager to the parent session (session.sessionManager) before exiting vibe mode.
- If persistence is intentionally impossible, exit vibe mode before any worker with a child session file exists, or clear the childSessionFile association.
- For ephemeral use, ensure records have no childSessionFile so the runtime can mark them terminal without persistence.
Example fix
// before
const session = createStubSession(); // sessionManager undefined
await vibe.exit(session); // throws when workers exist
// after
const session = createStubSession({ sessionManager: new SessionManager(sessionFile) });
await vibe.exit(session); Defensive patterns
Strategy: validation
Validate before calling
if (!session.sessionManager) {
throw new Error("Refusing vibe exit: session manager required to persist worker tombstones");
} Type guard
function canPersistTombstones(s: VibeParentSession): boolean {
return !!s.sessionManager;
} Try / catch
try {
await vibe.exit(session);
} catch (err) {
if (err instanceof ToolError && err.message.includes("without the parent session manager")) {
logger.warn("vibe exit skipped tombstone persistence; attach a session manager", { sessionId: session.getSessionId?.() });
} else throw err;
} Prevention
- Always construct embedded sessions with a real SessionManager
- Ensure workers are spawned only in persistent sessions when tombstones matter
- Add an integration test exiting vibe mode with live workers
When it happens
Trigger: Calling vibe exit/kill-all (→ #killAllLocked → #persistModeExit) on a parent session whose sessionManager is unset, while at least one worker record still carries a childSessionFile that needs a tombstone appended.
Common situations: SDK embeddings that build a VibeParentSession without wiring a session manager; ephemeral/in-memory sessions used in tests; sessions opened in a mode that disables persistence (e.g. no session file configured).
Related errors
- Cleanse session could not be persisted
- Failed to persist ${sourceName} session
- Agent "${resolvedAgentId}" is already owned by another sessi
- Vibe parent session changed before mode exit could be persis
- Vibe session "${record.id}" changed parent scope before term
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/bfa3761df3af54b7.
Report an issue: GitHub.