can1357/oh-my-pi · error · ToolError

Invalid cmux socket response: expected object

Error message

Invalid cmux socket response: expected object

What it means

After a reply line parses as JSON, #parseResponse requires the payload to be a JSON object. Scalars, arrays, strings, or null payloads cannot carry the {ok, result, error} envelope, so this ToolError is thrown. It signals the peer speaks JSON but not the cmux response envelope.

Solutions

  1. Confirm the endpoint is the cmux daemon and the method is expected to return the {ok,...} envelope
  2. Upgrade or downgrade the daemon so client/server protocol versions align
  3. Inspect the raw reply (enable socket logging) to see what shape came back, then correct the caller or file a daemon bug
Defensive patterns

Strategy: type-guard

Type guard

function isCmuxEnvelope(v: unknown): v is { ok: boolean; result?: unknown; error?: { code?: string; message?: string; details?: unknown } } {
  return typeof v === "object" && v !== null && "ok" in v && typeof (v as { ok: unknown }).ok === "boolean";
}

Try / catch

try {
  return await client.request(method, params);
} catch (err) {
  if (err instanceof ToolError && err.message === "Invalid cmux socket response: expected object") {
    // peer is not speaking the cmux envelope: check endpoint and protocol version
  }
  throw err;
}

Prevention

When it happens

Trigger: Daemon returns a bare JSON array/string/null for a method; a different JSON-RPC-ish service on the socket replies with non-envelope JSON (e.g. {"jsonrpc":"2.0",...} without ok, or plain [1,2,3]).

Common situations: Pointing the client at a non-cmux JSON service on the same path/port; daemon regression emitting envelopes without ok for some methods; protocol version skew after an upgrade.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/6299142cba1b372b. Report an issue: GitHub.

Appendix: source

Thrown at packages/coding-agent/src/tools/browser/cmux/socket-client.ts:394

				line = line.slice(0, -1);
			}
			const waiter = this.#lineWaiters.shift();
			waiter?.resolve(line);
		}
	}

	#parseResponse(line: string): Record<string, unknown> {
		if (line.startsWith("ERROR:")) {
			throw new ToolError(line);
		}
		let payload: unknown;
		try {
			payload = JSON.parse(line);
		} catch (err) {
			throw new ToolError(`Invalid cmux socket JSON response: ${err instanceof Error ? err.message : String(err)}`);
		}
		if (!payload || typeof payload !== "object") {
			throw new ToolError("Invalid cmux socket response: expected object");
		}
		const response = payload as { ok?: unknown; result?: unknown; error?: CmuxErrorPayload };
		if (response.ok === true) {
			return (response.result ?? {}) as Record<string, unknown>;
		}
		if (response.ok === false) {
			throw new ToolError(formatCmuxError(response.error));
		}
		throw new ToolError("Invalid cmux socket response: missing ok flag");
	}

	#handleSocketFailure(err: Error): void {
		if (this.#disposed) return;
		this.#connected = false;
		this.#connectPromise = null;
		this.#rejectAll(new ToolError(`cmux socket error: ${err.message}`));
		this.#socket?.destroy();
		this.#socket = null;

View on GitHub (pinned to 9690622007)