JuliusBrussee/caveman · error
caveman agent: Cave Runtime did not become ready at ${gatewa
Error message
caveman agent: Cave Runtime did not become ready at ${gatewayURL}; run caveman setup --install What it means
Thrown after the runtime spawned a detached `caveman start` and polled the gateway every 100ms for a 10-second deadline without it ever becoming Caveman-ready (runtimeReady kept returning undefined and no early startupFailure fired). The gateway process either crashed after spawn, never bound the expected port, or was too slow to boot; the message directs you to caveman setup --install.
Source
Thrown at packages/agent/src/runtime.ts:5269
"caveman agent: Caveman CLI not found; run npm install, then caveman setup --install",
)
: new Error(`caveman agent: Cave Runtime failed to start (${error.message})`);
});
child.once("exit", (code, signal) => {
if (startupFailure !== undefined || code === 0) return;
startupFailure = new Error(
`caveman agent: Cave Runtime failed to start (caveman start exited ${signal ?? code}); run caveman setup --install`,
);
});
child.unref();
const deadline = Date.now() + 10_000;
while (Date.now() < deadline) {
await new Promise((resolve) => setTimeout(resolve, 100));
if (startupFailure !== undefined) throw startupFailure;
const startedBilling = await runtimeReady(gatewayURL, fetchImpl);
if (startedBilling !== undefined) return startedBilling;
}
throw new Error(
`caveman agent: Cave Runtime did not become ready at ${gatewayURL}; run caveman setup --install`,
);
}
async function runtimeReady(
gatewayURL: string,
fetchImpl: typeof globalThis.fetch,
): Promise<GatewayProviderBilling | undefined> {
const identity = await gatewayIdentity(gatewayURL, fetchImpl);
if (identity === undefined) return undefined;
return await localProxyOwned(new URL(gatewayURL)) ? identity.providerBilling : undefined;
}
// A gateway hostname is loopback only if the traffic can never leave the host.
// WHATWG URL returns IPv6 literals bracketed ("[::1]"), so both forms are
// checked; 127.0.0.0/8 and 0.0.0.0 all route to localhost and must not be
// treated as remote (nor as needing https).
function isLoopbackHostname(hostname: string): boolean {View on GitHub (pinned to 766dce6b13)
Solutions
- Run caveman status / caveman doctor and look at why the started process died (logs, port already in use)
- Free the gateway port (kill the stale caveman process) and retry
- Pre-warm the gateway outside the run: caveman start, then poll /health/ready yourself with a longer deadline, then invoke the run
- Reinstall once with caveman setup --install to rule out a broken runtime install
Example fix
# before: cold box, first run pays the 10s startup deadline and fails # after: pre-warm in a CI step, then run caveman setup --install caveman start until curl -fsS http://127.0.0.1:$PORT/health/ready >/dev/null; do sleep 0.2; done
Defensive patterns
Strategy: retry
Validate before calling
async function startGatewayAndWait(gatewayURL, timeoutMs = 30_000) {
spawn('caveman', ['start'], { detached: true, stdio: 'ignore' }).unref();
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
const res = await fetch(gatewayURL + '/health/ready').catch(() => null);
if (res && res.ok) return;
await new Promise((r) => setTimeout(r, 100));
}
throw new Error('gateway not ready within ' + timeoutMs + 'ms');
} Try / catch
try {
await run(agent, options);
} catch (error) {
if (error instanceof Error && error.message.includes('did not become ready')) {
await startGatewayAndWait(gatewayURL); // pre-warm with your own, longer deadline
return run(agent, options);
}
throw error;
} Prevention
- Warm the gateway in a pre-run CI step so the framework's 10s startup deadline is never the first boot
- Ensure the gateway port is free before runs (stale caveman processes are the usual culprit)
- On slow machines/containers, manage the runtime yourself with ensureRuntime:false after your own readiness poll
When it happens
Trigger: Loopback ensure path: spawn succeeded, but /health/ready at gatewayURL did not report a Caveman identity owned by this proxy within Date.now() + 10_000 ms — cold machines, port conflicts, gateway crashing during boot, or the URL pointing at a different port than the gateway bound.
Common situations: Cold CI container with slow first boot; a stale caveman process holding the port so the new one exits immediately; gateway URL/port misconfiguration; heavily loaded developer machine where 10s is not enough for first boot.
Related errors
- caveman agent: Cave Runtime failed to start (${error instanc
- invalid run-state contract
- caveman trial proxy did not become ready on ${host}:${port}
- cave_stale_lock:${checked.stale.join(",")}: run npm run buil
- caveman agent: tool timeoutMs must be a positive integer
AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18).
Data as JSON: /api/errors/5250ccaac965f073.
Report an issue: GitHub.