JuliusBrussee/caveman · error · Error
recent stats was not a JSON array
Error message
recent stats was not a JSON array
What it means
readRecentProxyRows executes `<proxy-bin> stats --recent N --json` via execFileSync and requires the parsed stdout to be a top-level JSON array of proxy rows. If the proxy prints an error object, plain text, or a non-array document, the Array.isArray check fails and this error is thrown. The catch block around it additionally surfaces stdout/stderr/status for diagnosis.
Source
Thrown at packages/cli/src/index.ts:15124
const seconds = Math.ceil(timeoutMs / 1000);
console.error(`no request seen through the proxy in ${seconds}s`);
console.error("hint: check base URL points at the Caveman gateway and run `caveman start`");
process.exit(1);
}
function verifyTimeoutMs(values: string[]): number {
const ms = Number(flagFrom(values, "--timeout-ms", ""));
if (Number.isFinite(ms) && ms > 0) return ms;
const seconds = Number(flagFrom(values, "--timeout", "60"));
if (Number.isFinite(seconds) && seconds > 0) return Math.round(seconds * 1000);
return 60_000;
}
function readRecentProxyRows(bin: string, recent: string): RecentProxyRow[] {
try {
const out = execFileSync(bin, ["stats", "--recent", recent, "--json"], { encoding: "utf8", env: process.env });
const parsed = JSON.parse(out);
if (!Array.isArray(parsed)) throw new Error("recent stats was not a JSON array");
return parsed.map((row) => ({
ts: String(row?.ts ?? ""),
agent_slug: String(row?.agent_slug ?? ""),
provider: String(row?.provider ?? ""),
model: String(row?.model ?? ""),
endpoint: String(row?.endpoint ?? ""),
input_tokens: Number(row?.input_tokens ?? 0),
output_tokens: Number(row?.output_tokens ?? 0),
basis: String(row?.basis ?? "inferred"),
}));
} catch (error) {
const e = error as { stdout?: string; stderr?: string; status?: number; message?: string };
if (e.stdout) process.stdout.write(e.stdout);
if (e.stderr) process.stderr.write(e.stderr);
if (!e.stdout && !e.stderr) console.error(`failed to read recent proxy requests via ${bin}: ${e.message ?? error}`);
process.exit(e.status ?? 1);
}
}View on GitHub (pinned to 5184b3d11a)
Solutions
- Run `<proxy-bin> stats --recent 5 --json` by hand and confirm it prints a JSON array
- Upgrade the proxy binary to a version that supports `stats --json`
- Make sure PATH / the proxy binary override points at the real caveman proxy
- Treat any non-array output as a contract break between CLI and proxy versions, not as empty data
Defensive patterns
Strategy: type-guard
Validate before calling
// Probe the stats surface before relying on it.
const out = execFileSync(bin, ['stats', '--recent', '1', '--json'], { encoding: 'utf8' });
if (!out.trim().startsWith('[')) throw new Error('proxy stats does not emit a JSON array — upgrade the proxy binary'); Type guard
const isRecentProxyRow = (r: any): r is RecentProxyRow => !!r && typeof r === 'object' && typeof r.ts === 'string' && typeof r.agent_slug === 'string' && Number.isFinite(r.input_tokens) && Number.isFinite(r.output_tokens); const isRowArray = (v: unknown): v is RecentProxyRow[] => Array.isArray(v) && v.every(isRecentProxyRow);
Try / catch
try {
rows = readRecentProxyRows(bin, recent);
} catch (e) {
if (String((e as Error).message).includes('not a JSON array')) {
// contract break with the proxy binary — check version, do not retry blindly
}
throw e;
} Prevention
- Pin CLI and proxy to the same release so the stats contract holds
- Compare `--version` output of both binaries before parsing stats
- Treat any non-array stats output as version skew, never as empty data
When it happens
Trigger: Proxy binary older than the CLI and lacking `stats --json` support; proxy emitting an error object instead of a row list; PATH (or the proxy env override) resolving a different binary with the same name; proxy printing null or a wrapper banner before the array.
Common situations: Mixed-version installs after a partial upgrade; wrapper scripts shadowing the real proxy binary; running stats on a freshly provisioned proxy before any rows exist (expected output is `[]`, anything else indicates skew).
Related errors
- invalid Decision Ledger response
- sync incomplete — ${failures.join("; ")}
- cave_stale_lock:${checked.stale.join(",")}: run npm run buil
- caveman agent: baseline model unavailable: ${provider}/${id}
- caveman agent: Cave Runtime failed to start (${error instanc
AI-assisted analysis of JuliusBrussee/caveman@5184b3d11a (2026-08-18).
Data as JSON: /api/errors/b6e90e6d30e508d1.
Report an issue: GitHub.