{"record":{"id":"2dd630a96ef7c593","repo":"can1357/oh-my-pi","slug":"vibe-mode-has-exited-enter-vibe-mode-again-before","errorCode":null,"errorMessage":"Vibe mode has exited; enter Vibe mode again before spawning a worker.","messagePattern":"Vibe mode has exited; enter Vibe mode again before spawning a worker\\.","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/vibe/runtime.ts","lineNumber":855,"sourceCode":"\t\t\t});\n\t\t\trestored++;\n\t\t}\n\t\treturn restored;\n\t}\n\n\t/** Spawn a persistent worker session and start its first turn in the background. */\n\tasync spawn(session: ToolSession, args: { cli: VibeCli; name?: string; prompt: string }): Promise<VibeSpawnOutcome> {\n\t\tconst scope = this.ownerScope(session);\n\t\treturn this.#withTerminationLock(scope, () => this.#spawnLocked(session, scope, args));\n\t}\n\n\tasync #spawnLocked(\n\t\tsession: ToolSession,\n\t\tscope: VibeOwnerScope,\n\t\targs: { cli: VibeCli; name?: string; prompt: string },\n\t): Promise<VibeSpawnOutcome> {\n\t\tif (this.#terminatedScopes.has(scopeKey(scope, \"\"))) {\n\t\t\tthrow new ToolError(\"Vibe mode has exited; enter Vibe mode again before spawning a worker.\");\n\t\t}\n\t\tconst manager = this.#manager(session);\n\t\tconst { agent, modelOverride, modelRole } = this.#resolveWorker(session, args.cli);\n\t\tif (!session.agentOutputManager) {\n\t\t\tsession.agentOutputManager = new AgentOutputManager(session.getArtifactsDir ?? (() => null));\n\t\t}\n\t\tconst reservedIds = this.#persistedIds(session, scope);\n\t\tfor (const ref of AgentRegistry.global().list()) reservedIds.add(ref.id);\n\t\tawait session.agentOutputManager.reserve(reservedIds);\n\t\tconst requestedName = args.name?.replace(/[^A-Za-z0-9_-]+/g, \"\").slice(0, 48);\n\t\tconst id = await session.agentOutputManager.allocate(requestedName || generateTaskName());\n\t\tconst parentSessionFile = scope.parentSessionFile;\n\t\tconst childSessionName = `${id}.jsonl`;\n\t\tconst childSessionFile = parentSessionFile\n\t\t\t? path.resolve(parentSessionFile.slice(0, -6), childSessionName)\n\t\t\t: undefined;\n\t\tconst createdAt = Date.now();\n\t\tconst record: VibeRecord = {","sourceCodeStart":837,"sourceCodeEnd":873,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/vibe/runtime.ts#L837-L873","documentation":"Once vibe mode has exited for a scope, the scope key (with empty id) is added to #terminatedScopes. #spawnLocked refuses to spawn into a terminated scope so that tombstoned workers cannot silently reappear. Re-entering vibe mode via the explicit entry path (activateScope) clears the flag and re-opens admission.","triggerScenarios":"Calling vibe_spawn (→ #spawnLocked) after a previous vibe exit on the same owner scope (same ownerId/parentSessionId/parentSessionFile) without an intervening explicit vibe-mode entry.","commonSituations":"An agent continues iterating and tries to spawn workers after the exit command; a resumed session replays a spawn after a persisted exit; retry logic re-issues a spawn that raced with the exit.","solutions":["Enter vibe mode again (the explicit vibe-mode entry tool/command) before spawning; this calls activateScope and clears the terminated flag.","Start a new vibe scope via the normal entry flow instead of reusing the exited one.","Check for code paths that call exit prematurely while spawns are still expected."],"exampleFix":"// before\nawait vibe.exit(session);\nawait vibe.spawn(session, { cli: \"fast\", prompt }); // throws\n// after\nawait vibe.exit(session);\nawait vibe.enter(session); // re-opens the scope\nawait vibe.spawn(session, { cli: \"fast\", prompt });","handlingStrategy":"try-catch","validationCode":"// no public predicate for terminated scopes; guard by re-entering after any exit\nawait vibe.enter(session); // activateScope clears the terminated flag\nawait vibe.spawn(session, { cli, prompt });","typeGuard":null,"tryCatchPattern":"try {\n  await vibe.spawn(session, { cli, prompt });\n} catch (err) {\n  if (err instanceof ToolError && err.message.includes(\"Vibe mode has exited\")) {\n    await vibe.enter(session);\n    await vibe.spawn(session, { cli, prompt });\n  } else throw err;\n}","preventionTips":["Treat vibe exit as terminal for the scope; always re-enter before new spawns","Avoid issuing spawns concurrently with an exit call","Model vibe lifecycle explicitly (enter → spawn/work → exit) in agent prompts"],"tags":["lifecycle","state","api-usage"],"backgroundTag":"scope-terminated","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}