headroomlabs-ai/headroom · error · Error
proxyUrl must not include a path, query, or hash
Error message
proxyUrl must not include a path, query, or hash
What it means
The proxyUrl is a valid http(s) URL but carries a path, query string, or fragment (anything where pathname !== '/', or search/hash present). Headroom proxy routing is origin-based; per-request paths cannot be encoded in proxyUrl, so the validator rejects it to avoid silently dropping those components.
Source
Thrown at plugins/openclaw/src/proxy-manager.ts:421
/** Parse a URL, returning the parsed object or throwing a descriptive error. */
function parseProxyUrl(proxyUrl: string): URL {
try {
return new URL(proxyUrl);
} catch {
throw new Error(`Invalid proxyUrl: "${proxyUrl}"`);
}
}
export function normalizeAndValidateProxyUrl(proxyUrl: string): string {
const parsed = parseProxyUrl(proxyUrl);
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
throw new Error("proxyUrl must use http:// or https://");
}
if (parsed.pathname !== "/" || parsed.search || parsed.hash) {
throw new Error("proxyUrl must not include a path, query, or hash");
}
return parsed.origin;
}
/** Returns true if the URL points to a local address (localhost or 127.0.0.1). */
export function isLocalProxyUrl(proxyUrl: string): boolean {
try {
const parsed = new URL(proxyUrl);
return parsed.hostname === "127.0.0.1" || parsed.hostname === "localhost";
} catch {
return false;
}
}
function withDefaultPort(proxyUrl: string, defaultPort: number): string {
const parsed = parseProxyUrl(proxyUrl);
if (!parsed.port) {View on GitHub (pinned to 322425c43b)
Solutions
- Strip the path/query/hash and pass only the origin: "http://127.0.0.1:8787"
- If auth is needed for the proxy, use the mechanism Headroom actually supports (e.g. headers/env config), not URL query params
- If Headroom sits behind a reverse proxy sub-path, expose it at a dedicated origin/port instead
Example fix
// before
manager.configure({ proxyUrl: "http://127.0.0.1:8787/proxy?token=abc" }); // throws
// after
manager.configure({ proxyUrl: "http://127.0.0.1:8787" }); Defensive patterns
Strategy: type-guard
Validate before calling
function isBareOrigin(value: string): boolean {
try {
const u = new URL(value);
return u.pathname === "/" && !u.search && !u.hash;
} catch {
return false;
}
}
if (!isBareOrigin(proxyUrl)) {
throw new Error(`proxyUrl must be an origin only (no path/query/hash): got ${proxyUrl}`); Type guard
function isOriginOnlyUrl(value: string): value is string {
try {
const u = new URL(value);
return u.pathname === "/" && !u.search && !u.hash;
} catch {
return false;
}
} Prevention
- Pass only the origin (scheme://host:port) as proxyUrl; a single trailing slash is the only allowed extra
- Do not encode proxy credentials or tokens in the URL query — use the supported auth mechanism
- If Headroom sits behind a sub-path reverse proxy, expose it at its own origin instead of encoding the path
When it happens
Trigger: Passing values like "http://127.0.0.1:8787/headroom", "http://host:8787/?token=abc", or "http://host:8787/#fragment" to normalizeAndValidateProxyUrl(). A trailing slash is fine (pathname '/'), any deeper path is not.
Common situations: Copy-pasting a dashboard or WS path from Headroom docs into proxyUrl; appending an auth token as a query param; reverse-proxy deployments where users try to include the mounted sub-path.
Related errors
- Invalid proxyUrl: "${proxyUrl}"
- proxyUrl must use http:// or https://
- proxyPort must be an integer between 1 and 65535
- Invalid {TOOL_INJECTION_STICKY_ENV}={normalized!r}; expected
- unsupported headroom-bench proxy mode: {raw_mode!r}
AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15).
Data as JSON: /api/errors/a7879f0a7f145354.
Report an issue: GitHub.