aaif-goose/goose · error
External ACP backend URL must not include query parameters…
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.
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 be the base URL before /acp
- External ACP backend URL must use http: or https:, got
- Failed to construct URL
- Invalid base URL
AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16).
Data as JSON: /api/errors/171647e0dcf91059.
Report an issue: GitHub.
Appendix: 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)