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

  1. Run `<proxy-bin> stats --recent 5 --json` by hand and confirm it prints a JSON array
  2. Upgrade the proxy binary to a version that supports `stats --json`
  3. Make sure PATH / the proxy binary override points at the real caveman proxy
  4. 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

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


AI-assisted analysis of JuliusBrussee/caveman@5184b3d11a (2026-08-18). Data as JSON: /api/errors/b6e90e6d30e508d1. Report an issue: GitHub.