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
- Set the value to a plain positive integer of seconds: CAVE_SETUP_TIMEOUT=300
- Unset the variable entirely to accept the 300-second default
- 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
- Document env values as bare seconds in .env templates
- Lint CI env matrices for empty strings leaking into numeric settings
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
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- ${envVar} does not point to an executable: ${explicit}
- cave_vercel_terminal_failure
- cave_breaker_threshold_invalid
- cave_breaker_retry_requires_budget
- cave_breaker_retry_spend_invalid
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/0233a30a576704b0.
Report an issue: GitHub.