can1357/oh-my-pi · error · LiveSignalingError

Codex live signaling returned no valid call ID

Error message

Codex live signaling returned no valid call ID

What it means

The signaling response must include a `Location` header containing a valid call ID (parsed by parseLiveCallId). If the header is missing or the ID doesn't match the expected format, the transport cannot address the call on the sideband channel and throws LiveSignalingError.

Source

Thrown at packages/coding-agent/src/live/transport.ts:231

			method: "POST",
			headers,
			body: JSON.stringify({
				sdp: offer,
				session: buildLiveSessionPayload(this.#options.instructions, this.#options.voice),
			}),
			signal: this.#options.signal,
		});
		const responseBody = await response.text();
		if (!response.ok) {
			const detail = boundedErrorBody(responseBody, response.statusText);
			throw new LiveSignalingError(response.status, `Codex live signaling failed (${response.status}): ${detail}`);
		}
		const answer = responseBody;
		if (!answer.trim())
			throw new LiveSignalingError(response.status, "Codex live signaling returned an empty SDP answer");
		const callId = parseLiveCallId(response.headers.get("location"));
		if (!callId) {
			throw new LiveSignalingError(response.status, "Codex live signaling returned no valid call ID");
		}
		return { answer, callId, access, attestation };
	}

	async #connectSideband(callId: string, access: OAuthAccess, attestation: string | undefined): Promise<void> {
		let failure = new Error("Codex live sideband connection failed");
		for (let attempt = 0; attempt < SIDEBAND_CONNECT_ATTEMPTS; attempt++) {
			try {
				await this.#openSideband(callId, access, attestation);
				return;
			} catch (cause) {
				failure = cause instanceof Error ? cause : new Error(String(cause));
				if (this.#options.signal?.aborted) throw abortReason(this.#options.signal);
				if (attempt + 1 < SIDEBAND_CONNECT_ATTEMPTS) await Bun.sleep(200 * 2 ** attempt);
			}
		}
		throw failure;
	}

View on GitHub (pinned to 9690622007)

Solutions

  1. Log all response headers to confirm what the server returned.
  2. Update the client to the latest version so parseLiveCallId matches the current header format.
  3. Retry — if a gateway intermittently strips headers, retrying may succeed.
  4. Verify no proxy strips or rewrites the Location header.

Example fix

// before
const { answer, callId } = await transport.signal(offer); // throws
// after
try {
  const { answer, callId } = await transport.signal(offer);
} catch (e) {
  if (e instanceof LiveSignalingError && e.message.includes("call ID")) {
    logger.warn("signaling missing call id; upgrading client may help");
  }
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// validate the Location header yourself after a raw signaling call if reimplementing
const loc = response.headers.get("location");
const callId = loc?.match(/([0-9a-f-]{16,})/i)?.[1];
if (!callId) throw new Error(`signaling response missing call id; location=${loc}`);

Type guard

const hasCallId = (r: { headers: Headers }): r is { headers: Headers } & { callId: string } =>
  Boolean(r.headers.get("location"));

Try / catch

try {
  const { answer, callId } = await transport.signal(offer);
} catch (e) {
  if (e instanceof LiveSignalingError && e.message.includes("no valid call ID")) {
    logger.error("signaling contract mismatch; check client version and proxies", {});
  }
  throw e;
}

Prevention

When it happens

Trigger: Signaling server returns 2xx with an SDP answer but no `location` header, a relative/malformed URL, or a call ID in an unexpected format (service version change).

Common situations: Codex live API contract change, misconfigured gateway dropping headers, older signaling backend that locates call IDs differently.

Related errors


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