can1357/oh-my-pi · error · ToolError
Vibe session "${record.id}" changed parent scope before term
Error message
Vibe session "${record.id}" changed parent scope before termination. What it means
For each pending worker record with a childSessionFile, #persistModeExit recomputes the expected child session path from the CURRENT parent session file and compares it with the path recorded at spawn time. A mismatch means the parent session file moved/changed after the worker was spawned, so tombstones would land in the wrong place; it throws naming the offending record id.
Source
Thrown at packages/coding-agent/src/vibe/runtime.ts:510
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.childSessionFile
) {
throw new ToolError(`Vibe session "${record.id}" changed parent scope before termination.`);
}
}
const appendEntriesAtomically = sessionManager.appendEntriesAtomically;
if (!appendEntriesAtomically) {
throw new ToolError("Vibe mode exit requires atomic parent-session persistence.");
}
await appendEntriesAtomically.call(sessionManager, () => {
for (const record of persistedPending) {
sessionManager.appendCustomEntry(VIBE_LIFECYCLE_CUSTOM_TYPE, {
...this.#eventBase(record),
action: "tombstone",
reason: "mode-exit",
});
}
sessionManager.appendModeChange?.("none");
});
for (const record of pending) record.terminalPersisted = true;
}View on GitHub (pinned to 9690622007)
Solutions
- Keep the parent session file path stable while vibe workers are alive; if the file must move, exit vibe mode first.
- Purge or re-create worker records so childSessionFile matches the current parent file location before exiting.
- Check for double-resolution bugs: record.childSessionFile is expected to already be absolute and derived from the same parent file.
Example fix
// before renameSessionFile(session, newPath); // workers spawned under old path await vibe.exit(session); // throws for record id // after await vibe.exit(session); // persist tombstones first renameSessionFile(session, newPath);
Defensive patterns
Strategy: try-catch
Validate before calling
const parentFile = session.getSessionFile();
for (const rec of activeRecords) {
if (rec.childSessionFile && path.resolve(path.dirname(parentFile!), `${rec.id}.jsonl`) !== rec.childSessionFile) {
throw new Error(`Record ${rec.id} is parented to a different session file; exit vibe mode before moving the session`);
}
} Try / catch
try {
await vibe.exit(session);
} catch (err) {
if (err instanceof ToolError && /changed parent scope before termination/.test(err.message)) {
const id = err.message.match(/"([^"]+)"/)?.[1];
logger.warn("vibe worker parented to stale session file", { id });
} else throw err;
} Prevention
- Treat the parent session file as immutable while workers are alive
- Exit vibe mode before any operation that moves or renames the session file
- Don't mix worker records across forked sessions
When it happens
Trigger: vibe exit (→ #killAllLocked → #persistModeExit) where path.resolve(parentSessionFile minus .jsonl suffix, `${record.id}.jsonl`) differs from record.childSessionFile — typically because the parent session file changed between spawn and exit, or the record was built under a different session file.
Common situations: Session file relocated or renamed (resume-from-copy) while workers were alive; worker records rebuilt from persisted events under a new session file path; mixing records across forked parent sessions.
Related errors
- Cleanse session could not be persisted
- Failed to persist ${sourceName} session
- Vibe mode exit cannot persist worker tombstones without the
- Vibe mode exit requires atomic parent-session persistence.
- Vibe parent session changed before the worker could start.
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/d7fb8bea1477d8e5.
Report an issue: GitHub.