headroomlabs-ai/headroom · error · Error
Attempted to start Headroom proxy, but it was not reachable…
Error message
Attempted to start Headroom proxy, but it was not reachable at ${startupUrl} (${startedProbe.reason ?? "unknown"}). What it means
Auto-start spawned the Headroom launcher, then polled waitForHeadroomProxy for startupTimeoutMs (default 20 000 ms), but the proxy never became reachable-and-identified-as-Headroom. The reason string from the final probe distinguishes 'nothing listening' from 'something answered but not Headroom'. The spawned process is detached, so it keeps running even after this failure.
Solutions
- Run the launcher manually with the same arguments (headroom proxy --host 127.0.0.1 --port 8787) and read its startup output — it usually reveals the crash or config error
- Increase startupTimeoutMs in the proxy manager config (e.g. 60000) for slow machines/CI
- Verify headroom-ai is installed and up to date (npm ls -g headroom-ai or pip show headroom-ai), reinstall if the binary is broken
- Kill any half-started detached Headroom processes from earlier attempts before retrying
Example fix
// before
manager.configure({ autoStart: true }); // startupTimeoutMs defaults to 20s
// after
manager.configure({ autoStart: true, startupTimeoutMs: 60_000 }); Defensive patterns
Strategy: retry
Validate before calling
import { waitForHeadroomProxy } from "./proxy-probe.js";
// Before relying on the manager, give a slow first start room
const ok = await waitForHeadroomProxy("http://127.0.0.1:8787", 60_000);
if (!ok.reachable || !ok.isHeadroom) {
throw new Error(`Headroom did not come up in 60s (${ok.reason}) — check launcher install`); Type guard
interface Probe { reachable: boolean; isHeadroom: boolean; reason?: string }
function proxyStarted(probe: Probe): probe is Probe & { reachable: true; isHeadroom: true } {
return probe.reachable && probe.isHeadroom;
} Try / catch
for (let attempt = 1; attempt <= 3; attempt++) {
try {
const url = await manager.resolveProxyUrl();
break; // success
} catch (e) {
const msg = e instanceof Error ? e.message : "";
if (msg.includes("not reachable at") && attempt < 3) {
await sleep(2_000 * attempt); // slow first start — back off and retry
continue;
}
throw e;
}
} Prevention
- Raise startupTimeoutMs on CI and cold-cache machines (60s instead of the 20s default)
- Verify 'headroom proxy' starts cleanly by hand once per environment so launcher failures surface early
- Keep headroom-ai's runtime deps warm (pre-install) in CI images to cut first-start latency
When it happens
Trigger: autoStart true, launcher spawn succeeded (no error from startHeadroomProxy), but within startupTimeoutMs the proxy either did not bind yet (slow install, cold npm resolution, first-run setup) or started then crashed, or started on a different host/port than probed.
Common situations: First run on a machine where headroom-ai still needs to download/compile; slow disk or npm global resolution delay; proxy crash on startup due to config error; startupTimeoutMs left at default on slow CI runners; detached leftover process from a previous failed attempt holding state.
Related errors
- Cannot auto-start Headroom at
- No usable Headroom launcher found. Tried PATH, local npm…
- Headroom proxy not detected on default endpoints
- Headroom proxy not reachable at
- Headroom proxy startup is disabled
AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15).
Data as JSON: /api/errors/8ad44768f7495a68.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/openclaw/src/proxy-manager.ts:137
`Cannot auto-start Headroom at ${startupUrl}: port is in use by a non-Headroom service (${startupProbe.reason ?? "unknown service"}).`,
);
}
this.logger.info(
`No Headroom proxy detected${explicitUrl ? ` at ${startupUrl}` : " on default local endpoints"}; attempting to auto-start...`,
);
await this.startHeadroomProxy(startupUrl, port);
const startedProbe = await waitForHeadroomProxy(
startupUrl,
this.config.startupTimeoutMs ?? 20_000,
);
if (startedProbe.reachable && startedProbe.isHeadroom) {
this.proxyUrl = startupUrl;
this.logger.info(`Headroom proxy started and reachable at ${startupUrl}`);
return startupUrl;
}
throw new Error(
`Attempted to start Headroom proxy, but it was not reachable at ${startupUrl} (${startedProbe.reason ?? "unknown"}).`,
);
}
if (explicitUrl) {
throw new Error(
`Headroom proxy not reachable at ${explicitUrl}. Ensure the proxy is running first.`,
);
}
throw new Error(
`Headroom proxy not detected on default endpoints (${defaultCandidates.join(", ")}). ` +
"Set proxyUrl explicitly or enable autoStart.",
);
}
private getProxyPort(): number {
const rawPort = this.config.proxyPort;View on GitHub (pinned to 322425c43b)