JuliusBrussee/caveman · error · Error

CAVE_SETUP_TIMEOUT must be a positive integer

Error message

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

What it means

The installer reads the optional CAVE_SETUP_TIMEOUT env var (seconds) and converts it to milliseconds. `timeoutMs` throws when the variable is set but not parseable as a positive integer, since a bogus value would silently break download timeouts.

Solutions

  1. Set CAVE_SETUP_TIMEOUT to a plain positive integer, e.g. 300
  2. Remove the variable entirely to use the default of 300 seconds
  3. Fix the CI/env file so no stray characters (units, spaces, quotes) leak into the value
  4. If you need sub-second precision, the variable doesn't support it — keep whole seconds

Example fix

// before
CAVE_SETUP_TIMEOUT=90s
// after
CAVE_SETUP_TIMEOUT=90
Defensive patterns

Strategy: validation

Validate before calling

const raw = process.env.CAVE_SETUP_TIMEOUT;
if (raw !== undefined && (!/^[1-9]\d*$/.test(raw.trim()))) {
  throw new Error(`CAVE_SETUP_TIMEOUT must be a positive integer, got ${JSON.stringify(raw)}`);
}

Try / catch

try {
  await setup();
} catch (e) {
  if (String(e.message).startsWith("CAVE_SETUP_TIMEOUT must be a positive integer")) {
    console.error("Fix CAVE_SETUP_TIMEOUT (plain integer seconds) or unset it for the 300s default");
  } else throw e;
}

Prevention

When it happens

Trigger: CAVE_SETUP_TIMEOUT set to a non-integer ("12.5", "abc", ""), zero, a negative number, or a value with spaces/units ("300s").

Common situations: Typo in CI config adding a unit suffix; empty string from an unset-but-exported variable; decimal entered by someone expecting sub-second precision; locale-formatted number.

Understand the failure class

Background: "is not a valid" / "Invalid ... value" environment variable errors: how libraries validate env vars and what to do when they reject yours — this error's family across 48 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/0233a30a576704b0. Report an issue: GitHub.

Appendix: 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 3ee70a1026)