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

  1. Strip everything after the path: keep only scheme://host[:port]/path.
  2. Pass tokens/credentials through the mechanism the backend actually supports, not URL query params.
  3. 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

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


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)