JuliusBrussee/caveman · error

CAVE_SETUP_TIMEOUT must be a positive integer (got ${JSON.st

Error message

CAVE_SETUP_TIMEOUT must be a positive integer (got ${JSON.stringify(raw)})

What it means

timeoutMs() parses the CAVE_SETUP_TIMEOUT environment variable (default 300 seconds) and throws unless Number() of it is a positive integer. This guards the AbortSignal.timeout used for every installer fetch, so a garbage value fails before any download starts rather than mid-install.

Source

Thrown at packages/shared/binary-installer/installer.mjs:82

export function targetPlatform(os = process.platform, nodeArch = process.arch) {
  const arch = nodeArch === "x64" ? "amd64" : nodeArch;
  if (!(os === "darwin" || os === "linux" || os === "win32") ||
      !(arch === "arm64" || arch === "amd64")) {
    throw new Error(`no prebuilt binary for ${os}/${arch} — supported: darwin/arm64, darwin/amd64, linux/arm64, linux/amd64, win32/arm64, win32/amd64`);
  }
  return { os, arch };
}

export function binaryInstallFilename(name, os = process.platform) {
  return os === "win32" ? `${name}.exe` : name;
}

function timeoutMs() {
  const raw = process.env.CAVE_SETUP_TIMEOUT ?? "300";
  const seconds = Number(raw);
  if (!Number.isInteger(seconds) || seconds <= 0) {
    throw new Error(`CAVE_SETUP_TIMEOUT must be a positive integer (got ${JSON.stringify(raw)})`);
  }
  return seconds * 1000;
}

async function asset(url, timeout) {
  let response;
  try {
    response = await fetch(url, { signal: AbortSignal.timeout(timeout) });
  } catch (error) {
    throw new Error(`binary download failed: ${error.message}`);
  }
  if (!response.ok) throw new Error(`binary download failed: HTTP ${response.status}`);
  return response;
}

function signedDigest(checksums, bundleRaw) {
  try {
    const bundle = JSON.parse(bundleRaw);

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Set the value to a plain positive integer of seconds: CAVE_SETUP_TIMEOUT=300
  2. Unset the variable entirely to accept the 300-second default
  3. Check for stray whitespace or units where the variable is defined (.env, compose file, CI settings)

Example fix

# before
CAVE_SETUP_TIMEOUT=300s   # or =0, =1.5

# after
CAVE_SETUP_TIMEOUT=300    # seconds, integer > 0 — or omit entirely
Defensive patterns

Strategy: validation

Validate before calling

if (process.env.CAVE_SETUP_TIMEOUT !== undefined) {
  const n = Number(process.env.CAVE_SETUP_TIMEOUT);
  if (!Number.isInteger(n) || n <= 0) delete process.env.CAVE_SETUP_TIMEOUT; // fall back to 300
}

Type guard

function isValidTimeout(v) {
  if (v === undefined) return true; // the default applies
  const n = Number(v);
  return Number.isInteger(n) && n > 0;
}

Prevention

When it happens

Trigger: Setting CAVE_SETUP_TIMEOUT to an empty string, "abc", "30s", "0", "-5", "1.5", or values with trailing whitespace — Number() yields NaN, 0, negative, or non-integer and the check rejects it.

Common situations: Copy-pasting units into env vars ("300s", "5m"); Docker ENV or CI secret matrices injecting empty strings; shell quoting mishaps; assuming milliseconds.

Understand the failure class

Related errors


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