JuliusBrussee/caveman · error · Error
invalid listener
Error message
invalid listener
What it means
Internal sentinel thrown inside standaloneProxyEndpoint() when CAVEMAN_LISTEN (or the default PROXY_ADDR) does not parse as a valid host:port listener. The outer catch immediately converts it into the more descriptive 'invalid CAVEMAN_LISTEN ... expected host:port' error (index 555), so users normally never see this bare message. It validates hostname presence, integer port in 1-65535, and no path component.
Solutions
- Set CAVEMAN_LISTEN to a plain host:port, e.g. CAVEMAN_LISTEN=127.0.0.1:8080.
- Remove any path or scheme from the value (no 'localhost:8080/api', no 'http://...').
- Use a port between 1 and 65535.
- Unset CAVEMAN_LISTEN to fall back to the built-in default PROXY_ADDR.
Example fix
// before CAVEMAN_LISTEN=localhost:99999 caveman proxy // after CAVEMAN_LISTEN=127.0.0.1:8080 caveman proxy
Defensive patterns
Strategy: validation
Validate before calling
function isValidListen(v: string | undefined): boolean {
if (!v) return true; // falls back to default
try {
const u = new URL(`http://${v.trim()}`);
const p = Number(u.port);
return !!u.hostname && Number.isInteger(p) && p >= 1 && p <= 65535 && u.pathname === "/";
} catch { return false; }
}
if (!isValidListen(process.env.CAVEMAN_LISTEN)) throw new Error("CAVEMAN_LISTEN must be host:port"); Try / catch
try {
startStandaloneProxy();
} catch (e) {
if (e instanceof Error && /invalid (listener|CAVEMAN_LISTEN)/.test(e.message)) {
console.error("Set CAVEMAN_LISTEN like 127.0.0.1:8080");
} else throw e;
} Prevention
- Always set CAVEMAN_LISTEN as plain host:port — no scheme, path, or extra whitespace
- Keep ports within 1-65535
- Echo the value (`echo "$CAVEMAN_LISTEN"`) in scripts to catch quoting mistakes
- Omit the variable entirely to use the default listener
When it happens
Trigger: CAVEMAN_LISTEN value, once wrapped as `http://${listen}` and parsed with new URL(), yields: empty hostname, non-integer or out-of-range port (0, 99999), or a non-"/" pathname (e.g. 'host:port/extra' or missing port).
Common situations: Setting CAVEMAN_LISTEN=":8080" variants whose port parses oddly, "localhost:0", "localhost:99999", or "localhost:8080/path" when launching the standalone proxy.
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 CAVEMAN_LISTEN
- 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/0c4b3a492edb7fbf.
Report an issue: GitHub.
Appendix: source
Thrown at packages/cli/src/index.ts:19030
// portListening probes a TCP port with a short timeout — used to detect whether
// the proxy is already up, so start/wrap can say so without false positives.
function portListening(host: string, port: number): Promise<boolean> {
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;View on GitHub (pinned to 3ee70a1026)