aaif-goose/goose · error
External ACP backend URL must not include query parameters o
Error message
External ACP backend URL must not include query parameters or fragments
What it means
Thrown by normalizeAcpHttpBaseUrl when the parsed URL contains a query string (url.search) and/or a fragment (url.hash). The base URL is used verbatim to construct .../status and .../acp endpoints, so any ?params or #fragment would either corrupt those paths or be silently dropped; the function rejects them up front.
Source
Thrown at ui/desktop/src/acp/url.ts:50
return false;
}
return octets.every((octet) => Number(octet) <= 255) && Number(octets[0]) === 127;
}
export function normalizeAcpHttpBaseUrl(rawBaseUrl: string): string {
const trimmed = rawBaseUrl.trim();
if (!trimmed) {
throw new Error('External ACP backend URL is required');
}
const url = new URL(trimmed);
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
throw new Error(`External ACP backend URL must use http: or https:, got ${url.protocol}`);
}
if (url.search || url.hash) {
throw new Error('External ACP backend URL must not include query parameters or fragments');
}
const pathname = url.pathname.replace(/\/+$/, '');
if (pathname.endsWith('/acp')) {
throw new Error('External ACP backend URL must be the base URL before /acp');
}
return `${url.origin}${pathname}`;
}
function httpEndpointUrlFromHttpBase(rawBaseUrl: string, endpoint: 'status' | 'acp'): string {
const baseUrl = normalizeAcpHttpBaseUrl(rawBaseUrl);
const url = new URL(baseUrl);
url.pathname = `${url.pathname.replace(/\/+$/, '')}/${endpoint}`;
return url.toString();
}
export function statusHttpUrlFromHttpBase(rawBaseUrl: string): string {View on GitHub (pinned to 3810898a74)
Solutions
- Strip everything after the path: keep only scheme://host[:port]/path.
- Pass tokens/credentials through the mechanism the backend actually supports, not URL query params.
- If callers may paste decorated URLs, strip search and hash before calling normalizeAcpHttpBaseUrl.
Example fix
// before
if (url.search || url.hash) {
throw new Error('External ACP backend URL must not include query parameters or fragments');
}
// after (caller sanitizes the pasted URL first)
const raw = new URL(userInput);
raw.search = '';
raw.hash = '';
const baseUrl = normalizeAcpHttpBaseUrl(raw.toString()); Defensive patterns
Strategy: validation
Validate before calling
// Strip query/fragment before validation
function cleanBaseUrl(raw: string): string {
const url = new URL(raw.trim());
url.search = '';
url.hash = '';
return url.toString();
} Type guard
function isBareHttpUrl(value: string): boolean {
try {
const url = new URL(value.trim());
return (url.protocol === 'http:' || url.protocol === 'https:') && !url.search && !url.hash;
} catch {
return false;
}
} Try / catch
try {
const base = normalizeAcpHttpBaseUrl(inputUrl);
} catch (error) {
if (/query parameters or fragments/.test(String(error))) {
return normalizeAcpHttpBaseUrl(cleanBaseUrl(inputUrl)); // retry with sanitized input
}
throw error;
} Prevention
- Sanitize pasted URLs (strip ? and #) at the input boundary.
- Never inline tokens as query params in the base URL.
- Use an input type/pattern that discourages decorated URLs.
When it happens
Trigger: Passing 'http://host:8080/?token=abc' or 'https://host/goose#section'; URLs copied from a browser address bar after navigating (fragments get appended); config values containing a token query parameter because the user tried to inline auth.
Common situations: Trying to embed an auth token in the URL (tokens belong elsewhere); pasting a URL with utm params or anchors; bookmarks adding fragments.
Related errors
- External ACP backend URL is required
- External ACP backend URL must use http: or https:, got ${url
- External ACP backend URL must be the base URL before /acp
- Invalid base URL: {}
- Failed to construct URL: {}
AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16).
Data as JSON: /api/errors/171647e0dcf91059.
Report an issue: GitHub.