JuliusBrussee/caveman · error · Error
invalid CAVEMAN_LISTEN
Error message
invalid CAVEMAN_LISTEN ${JSON.stringify(listen)}; expected host:port What it means
Thrown by standaloneProxyEndpoint() when the CAVEMAN_LISTEN environment variable cannot be interpreted as a host:port pair needed to bind the standalone Caveman proxy. The message includes the offending value JSON-quoted and states the expected 'host:port' format. It is the user-facing form of the internal 'invalid listener' guard.
Solutions
- Set CAVEMAN_LISTEN to exactly host:port, e.g. CAVEMAN_LISTEN=0.0.0.0:8080.
- Strip scheme, path, and whitespace from the value.
- Use a valid port number (1-65535) that is not privileged (>1024 unless root).
- Unset CAVEMAN_LISTEN to use the default listener address.
Example fix
// before export CAVEMAN_LISTEN="http://localhost:8080" caveman proxy # throws invalid CAVEMAN_LISTEN "http://localhost:8080"; expected host:port // after export CAVEMAN_LISTEN="localhost:8080" caveman proxy
Defensive patterns
Strategy: validation
Validate before calling
const listen = process.env.CAVEMAN_LISTEN;
if (listen && !/^[-.\w]+:(\d{1,5})$/.test(listen.trim()) || (listen && !(+listen.split(":")[1] >= 1 && +listen.split(":")[1] <= 65535))) {
throw new Error(`CAVEMAN_LISTEN "${listen}" is not host:port (e.g. 127.0.0.1:8080)`);
} Try / catch
try {
runProxy();
} catch (e) {
if (e instanceof Error && e.message.startsWith("invalid CAVEMAN_LISTEN")) {
console.error(e.message, "— expected e.g. CAVEMAN_LISTEN=127.0.0.1:8080");
} else throw e;
} Prevention
- Format CAVEMAN_LISTEN as host:port only (no http://, no path, no spaces)
- Use ports 1-65535; prefer >=1024 to avoid privileged-port issues
- Quote env values carefully in shells to avoid stray whitespace
- Unset CAVEMAN_LISTEN to accept the default PROXY_ADDR
When it happens
Trigger: CAVEMAN_LISTEN is set (or defaults to PROXY_ADDR) and fails URL-based validation: missing hostname, port not an integer, port outside 1-65535, or a path suffix; or `new URL('http://' + listen)` itself throws.
Common situations: typos like CAVEMAN_LISTEN="localhost 8080", "localhost:", ":8080" (empty hostname), "localhost:8080/", quoted values with stray spaces, or a full URL pasted in instead of host:port.
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
- invalid listener
- register requires CAVE_CONTROL_URL, CAVE_TOKEN, and…
- cave_claude_model_required
- caveman build: set CAVE_MODEL when zero or multiple…
- caveman-code: no supported provider credential found; set…
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/f2559d82c58a4492.
Report an issue: GitHub.
Appendix: source
Thrown at packages/cli/src/index.ts:19033
return new Promise((resolve) => {
const sock = netConnect({ host, port });
const finish = (v: boolean) => { sock.destroy(); resolve(v); };
sock.setTimeout(600);
sock.once("connect", () => finish(true));
sock.once("timeout", () => finish(false));
sock.once("error", () => finish(false));
});
}
function standaloneProxyEndpoint(): { host: string; port: number; listen: string } {
const listen = (process.env.CAVEMAN_LISTEN ?? PROXY_ADDR).trim();
try {
const url = new URL(`http://${listen}`);
const port = Number(url.port);
if (!url.hostname || !Number.isInteger(port) || port < 1 || port > 65535 || url.pathname !== "/") throw new Error("invalid listener");
return { host: url.hostname, port, listen };
} catch {
throw new Error(`invalid CAVEMAN_LISTEN ${JSON.stringify(listen)}; expected host:port`);
}
}
export function gatewayHostPort(gw = gatewayURL()): { host: string; port: number } {
try {
const u = new URL(gw);
const defaultPort = u.protocol === "https:" ? 443 : 80;
return { host: u.hostname || "127.0.0.1", port: Number(u.port) || defaultPort };
} catch {
return { host: "127.0.0.1", port: 8787 };
}
}
function isCliEntrypoint(): boolean {
if (!process.argv[1]) return false;
const here = fileURLToPath(import.meta.url);
try {
return realpathSync(process.argv[1]) === here;View on GitHub (pinned to 3ee70a1026)