can1357/oh-my-pi · warning
Session disposed before launch completion delivery
Error message
Session disposed before launch completion delivery
What it means
The launch tool registers a completion sink that delivers daemon-launch completion notifications to the session. If the notification callback fires after the session has been disposed, it throws this error because a dead session can no longer receive the completion delivery. This is an internal lifecycle guard, not a user-input validation error.
Source
Thrown at packages/coding-agent/src/tools/hub/launch.ts:75
session: ToolSession,
client: DaemonBrokerClient,
owner: string,
): CompletionLease | undefined {
if (!session.queueLaunchCompletion) return undefined;
let clients = completionRegistrations.get(session);
if (!clients) {
clients = new Map();
completionRegistrations.set(session, clients);
}
let owners = clients.get(client);
if (!owners) {
owners = new Map();
clients.set(client, owners);
}
let registration = owners.get(owner);
if (!registration) {
const unregister = client.onCompletion(owner, notification => {
if (session.isDisposed?.()) throw new Error("Session disposed before launch completion delivery");
const delivery = session.queueLaunchCompletion?.(notification);
if (!delivery) throw new Error("Session cannot accept launch completion delivery");
return delivery;
});
let unregisterDispose: (() => void) | void;
let unregisterSessionChange: (() => void) | void;
const cleanup = (preservePending = false): void => {
if (!registration?.active) return;
registration.active = false;
unregister({ preservePending });
unregisterDispose?.();
unregisterSessionChange?.();
owners.delete(owner);
if (owners.size === 0) clients.delete(client);
if (clients.size === 0) completionRegistrations.delete(session);
};
registration = { inFlight: 0, retained: false, active: true, cleanup };
owners.set(owner, registration);View on GitHub (pinned to 9690622007)
Solutions
- Ignore if you intentionally aborted the session — the error reflects the abort racing completion and is expected.
- Retry the launch in a fresh, live session.
- Reduce the launch readiness wait (ready.log/ready.port) so completion lands before the session is disposed, or avoid disposing the session while a launch is pending.
Defensive patterns
Strategy: try-catch
Validate before calling
// Before disposing the session, ensure no launch is pending:
if (session.isDisposed?.()) throw new Error("cannot launch: session already disposed"); Try / catch
try {
await launchTool.run({ op: "start", name, application, ready });
} catch (err) {
if (err instanceof Error && err.message === "Session disposed before launch completion delivery") {
// session was aborted mid-launch; retry in a live session or ignore on abort
} else throw err;
} Prevention
- Don't dispose the session while a launch with readiness waits is pending.
- Keep readiness timeouts shorter than session lifetime expectations.
- Treat this error as expected noise when aborting mid-launch.
When it happens
Trigger: A launch completion notification arrives via client.onCompletion after `session.isDisposed()` returns true — i.e. the session was closed/cancelled while a daemon start was still in flight.
Common situations: User aborted or exited the agent session while a launch tool call was awaiting readiness; a timeout/cancellation disposed the session concurrently with the daemon finishing; long readiness waits (ready.log polling) racing session shutdown.
Related errors
- Cannot set cwd on a disposed JS runtime
- Server "${name}" was disconnected during initial connection
- Server "${name}" was disconnected during reconnection
- Python execution is unavailable while session disposal is in
- Recipient session is disposed.
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/210f7bd204f1123f.
Report an issue: GitHub.