can1357/oh-my-pi · error · Error

Claude command failed before /v1/messages completed: ${error

Error message

Claude command failed before /v1/messages completed: ${errorMessage(first.error)}${outputSuffix()}

What it means

When the PTY running the Claude command reports an error (pty-error) before any /v1/messages exchange was captured — including after a 250ms grace wait for a late capture — the trace throws with the command's failure message plus captured output. It distinguishes 'Claude crashed/errored' from the plain-exit case of the next error.

Source

Thrown at packages/coding-agent/src/cli/claude-trace-cli.ts:777

		const ptyRace = runPromise.then(
			() => ({ kind: "pty-exit" as const }),
			error => ({ kind: "pty-error" as const, error }),
		);
		const first = await Promise.race([captureRace, ptyRace]);
		if (first.kind === "capture") {
			await shutdownPty(session, runPromise);
			return first.exchange;
		}
		if (first.kind === "capture-error") {
			throw new Error(`${errorMessage(first.error)}${outputSuffix()}`);
		}
		const late = await Promise.race([captureRace, Bun.sleep(250).then(() => ({ kind: "late-timeout" as const }))]);
		if (late.kind === "capture") {
			await shutdownPty(session, runPromise);
			return late.exchange;
		}
		if (first.kind === "pty-error") {
			throw new Error(
				`Claude command failed before /v1/messages completed: ${errorMessage(first.error)}${outputSuffix()}`,
			);
		}
		throw new Error(`Claude command exited before /v1/messages completed${outputSuffix()}`);
	} finally {
		terminal.dispose();
		await proxy.stop();
	}
}

export async function runClaudeTraceCommand(args: ClaudeTraceCommandArgs = {}): Promise<void> {
	process.stderr.write(
		`Starting Claude trace proxy on ${args.host ?? DEFAULT_PROXY_HOST}:${args.port ?? DEFAULT_PROXY_PORT}\n`,
	);
	const exchange = await runClaudeMessagesCapture(args);
	const output = args.json ? `${JSON.stringify(exchange, null, 2)}\n` : formatCapturedMessagesExchange(exchange);
	process.stdout.write(output.endsWith("\n") ? output : `${output}\n`);
}

View on GitHub (pinned to 9690622007)

Solutions

  1. Inspect errorMessage(first.error) and the captured output appended by outputSuffix() for the claude CLI's own error text.
  2. Verify `claude` runs standalone (`claude -p 'hi'`) with valid auth before retrying the trace.
  3. Check ANTHROPIC_API_KEY / logged-in state in the environment the trace spawns claude with.
  4. Increase --timeout or input delay if claude is failing before the injected prompt is delivered.

Example fix

// before: trace fails because claude is unauthenticated
$ omp claude-trace
// after: authenticate first
$ claude login && omp claude-trace
Defensive patterns

Strategy: validation

Validate before calling

// verify claude works before tracing
const probe = await Bun.$`claude -p 'hi'`.quiet().nothrow();
if (probe.exitCode !== 0) throw new Error(`claude CLI not usable: ${await probe.text()}`);

Try / catch

try {
  const exchange = await runTrace();
} catch (err) {
  if ((err as Error).message.includes("failed before /v1/messages completed")) {
    // fix auth/binary per the embedded error + captured output, then retry
  } else throw err;
}

Prevention

When it happens

Trigger: The spawned `claude` process inside the headless PTY exits with an error (bad auth, missing binary, crash, invalid API key) before completing a /v1/messages request that the MITM proxy could record.

Common situations: Expired or missing Anthropic credentials in the environment; `claude` CLI not on PATH or failing at startup; Claude Code hitting an immediate API error (401/403) before any exchange is captured; the one-word prompt rejected client-side.

Related errors


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