can1357/oh-my-pi · error
ACP session fork was cancelled: ${params.sessionId}
Error message
ACP session fork was cancelled: ${params.sessionId} What it means
Thrown by AcpAgent's fork handling (session/fork) when a freshly created session's switchSession() to the source session file returns false instead of loading it. AgentSession.switchSession() returns false only when a `session_before_switch` extension hook cancels the transition (reason "resume"). The fork is aborted and the throwaway session is disposed.
Source
Thrown at packages/coding-agent/src/modes/acp/acp-agent.ts:1283
const storedSession = await this.#findStoredSession(sessionId, cwd);
if (!storedSession) {
throw new Error(`ACP session not found: ${sessionId}`);
}
return await this.#openStoredSession(storedSession.path, cwd, mcpServers, sessionId);
}
async #forkManagedSession(params: ForkSessionRequest): Promise<ManagedSessionRecord> {
const sourcePath = await this.#resolveForkSourceSessionPath(params.sessionId);
const { session, setToolUIContext } = normalizeCreatedAcpSession(
await this.#createSession(path.resolve(params.cwd), {
interactivePrompts: this.#clientCapabilities?.elicitation?.form != null,
}),
);
try {
const success = await session.switchSession(sourcePath);
if (!success) {
throw new Error(`ACP session fork was cancelled: ${params.sessionId}`);
}
const forked = await session.fork();
if (!forked) {
throw new Error(`ACP session fork failed: ${params.sessionId}`);
}
} catch (error) {
await this.#disposeStandaloneSession(session);
throw error;
}
return await this.#registerPreparedSession(session, params.mcpServers ?? [], setToolUIContext);
}
async #openStoredSession(
sessionPath: string,
cwd: string,
mcpServers: McpServer[],
sessionId: string,
): Promise<ManagedSessionRecord> {View on GitHub (pinned to 9690622007)
Solutions
- Inspect installed extensions/plugins for `session_before_switch` handlers returning cancel:true and remove or narrow the handler (e.g. only cancel unrelated reasons).
- Retry the fork with extensions disabled to confirm the hook is the cause.
- If the hook is intentional, fork outside the guarded scope or expose a hook option that allows fork/source-adopt transitions.
Example fix
// extension: before
async function onSessionBeforeSwitch() {
return { cancel: true };
}
// after
async function onSessionBeforeSwitch(event) {
if (event.reason === "switch") return { cancel: true }; // allow fork/resume transitions
return { cancel: false };
} Defensive patterns
Strategy: try-catch
Validate before calling
// no pre-call check: hook cancellation is decided server-side; ensure no extension vetoes before forking
const hooksActive = extensions.some(e => e.handles("session_before_switch")); Try / catch
try {
await agent.fork({ sessionId });
} catch (err) {
if (err.message.includes("fork was cancelled")) {
// a session_before_switch hook vetoed the fork; disable/narrow the hook and retry
}
throw err;
} Prevention
- Audit extensions for session_before_switch cancel logic before enabling fork workflows.
- Keep hook cancel conditions narrow (specific reasons/sessions).
- Test fork flows with the full extension set enabled.
When it happens
Trigger: An ACP client sends a fork request whose source session resolves to a valid session file, but an extension/plugin registered a `session_before_switch` handler that returns { cancel: true } when the new session tries to adopt that file.
Common situations: A user has an extension hook that blocks session switches or forks (e.g. to protect the active session); a hook was written with overly broad matching (fires for reason "resume") during automated ACP clients.
Related errors
- ACP session fork failed: ${params.sessionId}
- ACP session load was cancelled: ${sessionId}
- ACP session fork is unavailable while a prompt is in progres
- ACP session cannot be forked before it is persisted: ${sessi
- Trusted extension failed to load: ${trustedExtensions.errors
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/103229a90807ad9c.
Report an issue: GitHub.