{"record":{"id":"cebffe7f78046cc9","repo":"can1357/oh-my-pi","slug":"acp-session-load-was-cancelled-sessionid","errorCode":null,"errorMessage":"ACP session load was cancelled: ${sessionId}","messagePattern":"ACP session load was cancelled: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/modes/acp/acp-agent.ts","lineNumber":1310,"sourceCode":"\t\t}\n\t\treturn await this.#registerPreparedSession(session, params.mcpServers ?? [], setToolUIContext);\n\t}\n\n\tasync #openStoredSession(\n\t\tsessionPath: string,\n\t\tcwd: string,\n\t\tmcpServers: McpServer[],\n\t\tsessionId: string,\n\t): Promise<ManagedSessionRecord> {\n\t\tconst { session, setToolUIContext } = normalizeCreatedAcpSession(\n\t\t\tawait this.#createSession(path.resolve(cwd), {\n\t\t\t\tinteractivePrompts: this.#clientCapabilities?.elicitation?.form != null,\n\t\t\t}),\n\t\t);\n\t\ttry {\n\t\t\tconst success = await session.switchSession(sessionPath);\n\t\t\tif (!success) {\n\t\t\t\tthrow new Error(`ACP session load was cancelled: ${sessionId}`);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tawait this.#disposeStandaloneSession(session);\n\t\t\tthrow error;\n\t\t}\n\t\treturn await this.#registerPreparedSession(session, mcpServers, setToolUIContext);\n\t}\n\n\tasync #registerPreparedSession(\n\t\tsession: AgentSession,\n\t\tmcpServers: McpServer[],\n\t\tsetToolUIContext: ((uiContext: ExtensionUIContext, hasUI: boolean) => void) | undefined,\n\t): Promise<ManagedSessionRecord> {\n\t\tconst record = this.#createManagedSessionRecord(session, setToolUIContext);\n\t\tsession.setClientBridge(createAcpClientBridge(this.#connection, session.sessionId, this.#clientCapabilities));\n\t\t// `record.lifetimeUnsubscribe` is installed in `#scheduleBootstrapUpdates`\n\t\t// so it shares the bootstrap race guard — see that comment for why.\n\t\ttry {","sourceCodeStart":1292,"sourceCodeEnd":1328,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/modes/acp/acp-agent.ts#L1292-L1328","documentation":"Thrown when opening a stored ACP session (session/load or session/resume path) and the new session's switchSession(sessionPath) returns false instead of loading the file. switchSession only returns false when a `session_before_switch` extension hook cancels the \"resume\" transition. The disposable session is then cleaned up and the error rethrown.","triggerScenarios":"Loading/resuming a stored session by path where an installed extension's `session_before_switch` handler returns { cancel: true } for reason \"resume\" targeting that session file.","commonSituations":"Guard rails in extensions that block resuming specific sessions (e.g. archived, shared, or cost-locked sessions); leftover hooks from a removed workflow.","solutions":["Find the extension registering `session_before_switch` and remove/narrow its cancel logic for reason \"resume\".","Resume with extensions disabled to confirm the hook is the blocker, then re-enable selectively.","If the veto is intentional, delete or archive the stored session file instead of resuming."],"exampleFix":"// before\nif (event.reason === \"resume\") return { cancel: true };\n// after\nif (event.reason === \"resume\" && blockedSessionFiles.has(event.targetSessionFile)) return { cancel: true };\nreturn { cancel: false };","handlingStrategy":"try-catch","validationCode":"// confirm no resume-vetoing hooks are active before load\nconst vetoing = extensions.some(e => e.handles(\"session_before_switch\") && e.cancels(\"resume\"));","typeGuard":null,"tryCatchPattern":"try {\n  await agent.load({ sessionId, cwd });\n} catch (err) {\n  if (err.message.includes(\"load was cancelled\")) {\n    // a session_before_switch hook blocked resuming this session file\n  }\n  throw err;\n}","preventionTips":["Review session_before_switch hooks when load/resume starts failing.","Scope hook vetoes to explicit session files, not all resumes.","Keep a mode to load sessions with extensions disabled for diagnosis."],"tags":["acp","session-load","cancelled-by-hook"],"backgroundTag":"operation-cancelled-by-hook","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}