JuliusBrussee/caveman · error · Error

empty version output

Error message

empty version output

What it means

runtimeVersionDiagnostic() parses the stdout of an external `caveman` runtime CLI to build a human-readable version string. Trimmed stdout was empty — the process produced no output — so there is nothing to report and it throws instead of fabricating a version. Any non-empty output is handled (JSON with version/binary_release, or a legacy plain line).

Source

Thrown at packages/agent/src/cli.ts:431

function compareNodeVersion(current: string, minimum: string): number {
  const parse = (value: string) => value.split(".").slice(0, 3).map((part) => Number(part));
  const left = parse(current);
  const right = parse(minimum);
  for (let index = 0; index < 3; index++) {
    const difference = (left[index] ?? 0) - (right[index] ?? 0);
    if (difference !== 0) return difference;
  }
  return 0;
}

function safeDiagnostic(error: unknown): string {
  const value = error instanceof Error ? error.message : String(error);
  return value.replace(/[\r\n]+/g, " ").slice(0, 512);
}

function runtimeVersionDiagnostic(stdout: string): string {
  const value = stdout.trim();
  if (value === "") throw new Error("empty version output");
  try {
    const parsed = JSON.parse(value) as { version?: unknown; binary_release?: unknown };
    if (typeof parsed.version === "string" && parsed.version.trim() !== "") {
      const release = typeof parsed.binary_release === "string" && parsed.binary_release.trim() !== ""
        ? ` (${parsed.binary_release.trim()})`
        : "";
      return `caveman ${parsed.version.trim()}${release}`;
    }
  } catch {
    // Older CLIs may return one plain version line.
  }
  return value.replace(/[\r\n]+/g, " ").slice(0, 256);
}

function credentialForModel(model: string): { name: string; available: boolean } {
  const provider = model.split("/", 1)[0];
  if (provider === "anthropic") {
    return { name: "ANTHROPIC_API_KEY", available: Boolean(process.env.ANTHROPIC_API_KEY) };

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Run the runtime binary directly (`caveman --version` or equivalent) and confirm it prints a version; if empty, reinstall it.
  2. Check PATH ordering for stub scripts shadowing the real caveman binary (which -a caveman).
  3. If a wrapper script is involved, make sure it forwards stdout instead of consuming it.

Example fix

# before: shim swallows output
#!/bin/sh
exec /opt/caveman/bin/caveman "$@" >/dev/null  # stdout discarded

# after
#!/bin/sh
exec /opt/caveman/bin/caveman "$@"
Defensive patterns

Strategy: validation

Validate before calling

import { execFileSync } from "node:child_process";
function assertRuntimeReportsVersion(bin = "caveman"): string {
  const out = execFileSync(bin, ["--version"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
  if (out === "") throw new Error(`${bin} produced no version output; check PATH shim`);
  return out;
}

Prevention

When it happens

Trigger: The version-probe subprocess (caveman runtime CLI) exits producing zero bytes on stdout: binary found but broken (crashes before printing), wrong executable shadowing the real one (an empty stub script earlier in PATH), or output redirected/swallowed by a wrapper.

Common situations: PATH pointing at a shim/wrapper script that swallows stdout; a half-installed or architecture-mismatched binary that dies silently; CI layer caching a truncated binary; Windows batch shim quirks.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/dc3471466a9ca00e. Report an issue: GitHub.