{"record":{"id":"db3820f7541d0820","repo":"headroomlabs-ai/headroom","slug":"remote-headroom-proxy-not-reachable-at-explicitu","errorCode":null,"errorMessage":"Remote Headroom proxy not reachable at ${explicitUrl}. Ensure the proxy is running at that address.","messagePattern":"Remote Headroom proxy not reachable at (.+?)\\. Ensure the proxy is running at that address\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"plugins/openclaw/src/proxy-manager.ts","lineNumber":108,"sourceCode":"      if (probe.reachable && probe.isHeadroom) {\n        this.proxyUrl = url;\n        this.logger.info(`Headroom proxy already running at ${url}`);\n        return url;\n      }\n    }\n\n    if (explicitUrl) {\n      const explicitProbe = probeByUrl.get(explicitUrl);\n      if (explicitProbe?.reachable && !explicitProbe.isHeadroom) {\n        throw new Error(\n          `Service reachable at ${explicitUrl}, but it does not appear to be a Headroom proxy (${explicitProbe.reason ?? \"unknown service\"}).`,\n        );\n      }\n    }\n\n    // Remote URLs are connect-only — never auto-start a subprocess for them\n    if (explicitUrl && !isLocalProxyUrl(explicitUrl)) {\n      throw new Error(\n        `Remote Headroom proxy not reachable at ${explicitUrl}. Ensure the proxy is running at that address.`,\n      );\n    }\n\n    // Auto-start is only available for local proxies\n    if (this.config.autoStart === true) {\n      const startupUrl = explicitUrl ?? defaultCandidates[0];\n      const startupProbe = probeByUrl.get(startupUrl);\n      if (startupProbe?.reachable && !startupProbe.isHeadroom) {\n        throw new Error(\n          `Cannot auto-start Headroom at ${startupUrl}: port is in use by a non-Headroom service (${startupProbe.reason ?? \"unknown service\"}).`,\n        );\n      }\n\n      this.logger.info(\n        `No Headroom proxy detected${explicitUrl ? ` at ${startupUrl}` : \" on default local endpoints\"}; attempting to auto-start...`,\n      );\n      await this.startHeadroomProxy(startupUrl, port);","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/headroomlabs-ai/headroom/blob/322425c43bffde1ed0b64fecf3cf5951565dd82b/plugins/openclaw/src/proxy-manager.ts#L90-L126","documentation":"A non-local proxyUrl (hostname other than 127.0.0.1/localhost) was configured, the probe could not reach it, and the manager refuses to auto-start anything for remote URLs by design — auto-start only ever spawns a local subprocess. The error tells you the remote proxy must be running and reachable before OpenClaw connects.","triggerScenarios":"Configuring proxyUrl like http://proxy.internal:8787 or http://10.0.0.5:8787 when nothing is listening there, when a firewall blocks it, when the hostname does not resolve, or when the remote Headroom process is not started yet.","commonSituations":"Shared/team Headroom proxy on a remote host that is down; Docker/K8s service name misspelled or not yet up when the client starts; VPN or network policy blocking the port; remote Headroom bound to 127.0.0.1 instead of 0.0.0.0.","solutions":["Start (or restart) the Headroom proxy on the remote host and confirm it binds an address reachable from the client (not just remote-loopback): headroom proxy --host 0.0.0.0 --port 8787","Verify reachability from the client machine: curl http://<host>:8787 — fix DNS, firewall, or VPN issues it reveals","If the proxy is actually local, use http://127.0.0.1:<port> so the manager can auto-start it","If the remote service starts slowly, delay client startup until it is up (health check before constructing the manager)"],"exampleFix":"# on the remote host — before: bound to loopback only\nheadroom proxy --host 127.0.0.1 --port 8787\n\n# after: bind so clients can reach it\nheadroom proxy --host 0.0.0.0 --port 8787","handlingStrategy":"validation","validationCode":"async function remoteProxyUp(url: string, timeoutMs = 3000): Promise<boolean> {\n  const ctrl = new AbortController();\n  const t = setTimeout(() => ctrl.abort(), timeoutMs);\n  try {\n    await fetch(url, { signal: ctrl.signal });\n    return true; // reachable at HTTP level; identity checked by the manager\n  } catch {\n    return false;\n  } finally {\n    clearTimeout(t);\n  }\n}\n\nconst url = \"http://proxy.internal:8787\";\nif (!await remoteProxyUp(url)) {\n  throw new Error(`Remote proxy ${url} not up yet; wait for it before starting the client`);","typeGuard":"import { isLocalProxyUrl } from \"./proxy-manager.js\";\n\nfunction requiresRunningProxy(proxyUrl: string): boolean {\n  // Remote URLs are connect-only: never expect auto-start for them\n  return !isLocalProxyUrl(proxyUrl);\n}","tryCatchPattern":"try {\n  await manager.resolveProxyUrl();\n} catch (e) {\n  if (e instanceof Error && e.message.includes(\"Remote Headroom proxy not reachable\")) {\n    // infrastructure issue: surface to ops, do not retry in-process\n    throw new Error(`Headroom proxy host is down or unreachable — check the remote service`);\n  }\n  throw e;\n}","preventionTips":["Add the remote proxy URL to your orchestration's dependency/health checks so the client starts only when it is up","Document that remote URLs never auto-start — startup ordering is the deployer's responsibility","Use a stable internal DNS name and monitor it, so a moved/rehosted proxy fails loudly in monitoring first"],"tags":["proxy","network","remote","configuration","openclaw"],"backgroundTag":null,"analyzedSha":"322425c43bffde1ed0b64fecf3cf5951565dd82b","analyzedAt":"2026-08-15T01:03:05.481Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}