can1357/oh-my-pi · info · Error
Handoff aborted by session
Error message
Handoff aborted by session
What it means
throwIfHandoffAborted is the catch-all abort translation: when the signal's reason is neither a plain AbortError, nor an Error, nor a non-empty string, it throws Error("Handoff aborted by session"). This indicates the owning AgentSession aborted the handoff in a way that carried no descriptive reason.
Source
Thrown at packages/coding-agent/src/session/session-handoff.ts:34
import { obfuscateProviderContext } from "../secrets/message-transform";
import type { SecretObfuscator } from "../secrets/obfuscator";
import type { HandoffResult, SessionHandoffOptions } from "./agent-session-types";
import type { SessionManager } from "./session-manager";
function createHandoffFileName(date = new Date()): string {
const fileTimestamp = date.toISOString().replace(/[:.]/g, "-");
return `handoff-${fileTimestamp}.md`;
}
function throwIfHandoffAborted(signal: AbortSignal): void {
if (!signal.aborted) return;
const reason = signal.reason;
if (reason instanceof DOMException && reason.name === "AbortError") {
throw new Error("Handoff cancelled");
}
if (reason instanceof Error) throw reason;
if (typeof reason === "string" && reason.length > 0) throw new Error(reason);
throw new Error("Handoff aborted by session");
}
/** Capabilities borrowed from the owning AgentSession. */
export interface SessionHandoffHost {
agent: Agent;
sessionManager: SessionManager;
settings: Settings;
modelRegistry: ModelRegistry;
sideStreamFn: StreamFn;
obfuscator: SecretObfuscator | undefined;
model(): Model | undefined;
thinkingLevel(): ThinkingLevel | undefined;
sessionId(): string;
baseSystemPrompt(): string[];
setSkipPostTurnMaintenance(timestamp: number | undefined): void;
obfuscateTextForProvider(text: string | undefined): string | undefined;
deobfuscateFromProvider(text: string): string;
convertMessagesToLlm(messages: AgentMessage[], signal?: AbortSignal): Promise<Message[]>;View on GitHub (pinned to 9690622007)
Solutions
- Treat as a session-level cancellation: stop handoff work and clean up
- Abort with a descriptive Error or string reason at the abort site so callers get a meaningful message
- Catch this error and retry the handoff with a new AbortController if the session is active again
- Audit the code paths that own the controller to see why the session aborted
Example fix
// before
controller.abort(true); // opaque -> 'Handoff aborted by session'
// after
controller.abort(new Error("Session shutting down")); // descriptive reason Defensive patterns
Strategy: try-catch
Validate before calling
if (handoffSignal.aborted) {
console.warn("Handoff aborted before start:", handoffSignal.reason);
return;
} Try / catch
try {
await handoff.generateDocument(handoffSignal);
} catch (err) {
if (err instanceof Error && err.message === "Handoff aborted by session") {
// session-level teardown: stop work, no retry
return;
}
throw err;
} Prevention
- Abort controllers with Error or string reasons so failures are diagnosable
- Treat any aborted-signal error as terminal for that handoff attempt
- Coordinate handoff lifetime with session teardown order
- Retry only with a fresh controller after the session is active again
When it happens
Trigger: generateDocument's throwIfHandoffAborted check runs after signal.abort() was called with a reason that is not an Error/DOMException/string (e.g. abort(true) with a non-string truthy value, or undefined).
Common situations: Session shutdown/teardown aborting in-flight handoff generation; custom abort reasons of unexpected type; library code aborting controllers without a reason.
Related errors
- Handoff cancelled
- Request was aborted
- Auth broker request aborted
- OAuth refresh ownership aborted by caller
- Request was aborted.
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/b7e6107ef2462d16.
Report an issue: GitHub.