can1357/oh-my-pi · error · ToolError

Vibe session "${record.id}" changed parent scope before its

Error message

Vibe session "${record.id}" changed parent scope before its turn started.

What it means

At the start of every vibe worker turn, #registerTurnJob appends a 'turn-started' lifecycle event to the parent session file via #appendLifecycleEvent, which validates the current parent scope against record.parentSessionFile. If the event cannot be persisted because the parent session was switched, branched, compacted, or its owner changed before the turn began, and the record has a childSessionFile, the turn aborts with this ToolError before spawning any work. The error is then wrapped into a VibeTurnError by the catch block.

Source

Thrown at packages/coding-agent/src/vibe/runtime.ts:1424

		const jobId = manager.register(
			"task",
			`vibe ${record.cli} ${record.id}: ${firstLine(message, 60)}`,
			async ({ jobId: ownJobId, signal }) => {
				record.state = "running";
				record.turnCount = turnIndex;
				record.lastActivityAt = Date.now();
				try {
					const turnStartedPersisted = await this.#appendLifecycleEvent(
						session,
						{
							...this.#eventBase(record),
							action: "turn-started",
							turn: turnIndex,
						},
						record.parentSessionFile,
					);
					if (record.childSessionFile && !turnStartedPersisted) {
						throw new ToolError(`Vibe session "${record.id}" changed parent scope before its turn started.`);
					}
					const result = options.first
						? await runSubprocess(await this.#buildSpawnOptions(session, record, message, signal, onProgress))
						: await runSubagentFollowUpTurn({
								id: record.id,
								agent: record.agent,
								message,
								description: `vibe ${record.cli} session`,
								signal,
								onProgress,
								eventBus: session.eventBus,
								subagentEventBus: session.subagentEventBus,
								artifactsDir: session.getSessionFile()?.slice(0, -6),
							});
					return await this.#settleTurn(session, manager, record, turn, ownJobId, turnIndex, result);
				} catch (error) {
					if (error instanceof VibeTurnError) throw error;
					await this.#finishTurn(session, manager, record, ownJobId);

View on GitHub (pinned to 9690622007)

Solutions

  1. Drain or kill vibe workers before switching/compacting the parent session; deliver queued turns in the original scope.
  2. Re-register the vibe worker under the current session scope (new record bound to the current parentSessionFile) and send the message there.
  3. Switch back to the original parent session file before sending the follow-up turn.
  4. If the turn is not essential, treat the wrapped VibeTurnError as terminal for that turn and start a fresh worker.

Example fix

// before: follow-up after session switch
await session.switchTo(newFile);
await vibe.send(workerId, "continue"); // turn fails: changed parent scope before its turn started
// after: send while scope matches, or respawn under the new scope
await vibe.send(workerId, "continue");
await session.switchTo(newFile);
Defensive patterns

Strategy: validation

Validate before calling

const current = session.getSessionFile();
const canRunTurn = !!current && record.parentSessionFile !== null &&
  path.resolve(current) === path.resolve(record.parentSessionFile) &&
  session.getSessionId?.() === record.parentSessionId;
if (!canRunTurn) throw new Error("parent scope changed; re-register worker before sending turns");

Type guard

null

Try / catch

try {
  await vibe.send(workerId, message);
} catch (error) {
  if (error instanceof VibeTurnError && error.message.includes("changed parent scope before its turn started")) {
    // restore scope or respawn the worker under the current session
  } else throw error;
}

Prevention

When it happens

Trigger: Sending a message/new turn to a vibe worker after the parent session scope changed: getSessionFile() resolves to a different path than record.parentSessionFile, or session id / owner agent id no longer match, so #appendLifecycleEvent returns false while record.childSessionFile is set.

Common situations: Queueing follow-up prompts to a background vibe worker and then switching sessions in the same process; session compaction replaced the session file between turns; the worker's owning agent changed; automation that switches sessions while vibe workers still have queued turns.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/8492d9a05d47da91. Report an issue: GitHub.