can1357/oh-my-pi · error · ToolError
cmux socket closed
Error message
cmux socket closed
What it means
CmuxSocketClient.connect() refuses to establish a new connection once the client has been disposed via close(), which sets the internal #disposed flag. This error signals the client instance is permanently shut down and cannot be reused. It is thrown immediately before any socket work is attempted.
Source
Thrown at packages/coding-agent/src/tools/browser/cmux/socket-client.ts:93
#connectPromise: Promise<void> | null = null;
#connected = false;
#disposed = false;
#buffer = "";
readonly #lineWaiters: LineWaiter[] = [];
readonly #queue: RequestJob[] = [];
#activeJob: RequestJob | null = null;
#pumping = false;
constructor(opts: { socketPath: string; password?: string; relayId?: string; relayToken?: string }) {
this.#socketPath = opts.socketPath;
this.#password = opts.password;
this.#relayId = opts.relayId ?? process.env.CMUX_RELAY_ID;
this.#relayToken = opts.relayToken ?? process.env.CMUX_RELAY_TOKEN;
}
async connect(): Promise<void> {
if (this.#disposed) {
throw new ToolError("cmux socket closed");
}
if (this.#connected && this.#socket && !this.#socket.destroyed) {
return;
}
if (this.#connectPromise) {
return await this.#connectPromise;
}
this.#connectPromise = this.#openSocket();
try {
await this.#connectPromise;
} finally {
this.#connectPromise = null;
}
}
async request(
method: string,View on GitHub (pinned to 9690622007)
Solutions
- Create a new CmuxSocketClient instance instead of reusing the disposed one
- Check whether the code path calling connect() runs after session teardown and gate it on session liveness
- If the close was unintentional, audit who calls close() (double-close or early cleanup) and fix the lifecycle
Example fix
// before
if (!this.client) this.client = new CmuxSocketClient({ socketPath });
await this.client.connect(); // throws if disposed earlier
// after
if (!this.client || this.isDisposed) {
this.client = new CmuxSocketClient({ socketPath });
}
await this.client.connect(); Defensive patterns
Strategy: try-catch
Try / catch
try {
await client.connect();
} catch (err) {
if (err instanceof ToolError && err.message === 'cmux socket closed') {
client = new CmuxSocketClient({ socketPath });
await client.connect();
} else throw err;
} Prevention
- Recreate the client instead of reusing one after close()
- Guard background pumps against running post-shutdown
When it happens
Trigger: Calling connect() (directly or indirectly through the request pump loop) after close() has been called on the same CmuxSocketClient instance. Also fires when openBrowserHandle reuses a stale, already-closed client.
Common situations: Holding a client across a browser-session teardown; a race where a background pump retries connect() after the user closed the session; caching a client in a module-level variable and reusing it after shutdown.
Related errors
- Cannot ${action} on a disposed JS runtime
- blob broker exposure failed to start
- DAP adapter ${this.adapter.name} is not running
- Debug session ${root.id} is still active. Terminate it befor
- Cannot set cwd on a disposed JS runtime
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/0d91ad0d070ac629.
Report an issue: GitHub.