aaif-goose/goose · critical
ACP URL is not available
Error message
ACP URL is not available
What it means
Thrown by openConnection() in the desktop UI when window.electron.getAcpUrl() resolves to a falsy value. That IPC call reaches main.ts (gooseServeLeases.getAcpUrl(windowId) ?? null), so the error means the Electron main process has no ACP WebSocket URL registered for this window: the goose serve backend either never started, exited, or the external-backend mode was not configured. The connection to the goose agent cannot even be attempted.
Source
Thrown at ui/desktop/src/acp/acpConnection.ts:132
if (!pendingConnection) {
const generation = connectionGeneration;
let connectionAttempt: Promise<AcpConnection>;
connectionAttempt = openConnection(generation).catch((error) => {
if (pendingConnection === connectionAttempt) {
pendingConnection = null;
}
throw error;
});
pendingConnection = connectionAttempt;
}
return pendingConnection;
}
async function openConnection(generation: number): Promise<AcpConnection> {
const wsUrl = await window.electron.getAcpUrl();
if (!wsUrl) {
throw new Error('ACP URL is not available');
}
// Electron treats an explicitly passed undefined protocol as a subprotocol.
const stream = createWebSocketStream(wsUrl, { protocols: [] });
const client = connectGooseAcpClient(stream, createClientCallbacks());
try {
const initializeResponse = await withTimeout(
client.connection.agent.request(methods.agent.initialize, {
protocolVersion: PROTOCOL_VERSION,
_meta: {
'goose/useLoginShellPath': true,
},
clientCapabilities: {
elicitation: { form: {} },
_meta: {
goose: {
mcpHostCapabilities: DEFAULT_GOOSE_MCP_HOST_CAPABILITIES,View on GitHub (pinned to 3810898a74)
Solutions
- Check main-process logs for the 'goose serve' spawn: if it exits immediately, fix the root cause (invalid config, missing binary, port conflict).
- If connecting to an external backend, set the external ACP base URL so getAcpUrl has a value instead of null.
- Retry the connection after a short delay — the lease is often registered milliseconds after the first attempt during startup.
- Verify the IPC handler 'get-acp-url' is registered before the renderer calls it and that the windowId matches an active lease.
Example fix
// before
const wsUrl = await window.electron.getAcpUrl();
if (!wsUrl) {
throw new Error('ACP URL is not available');
}
// after (brief bounded retry for the startup race)
let wsUrl: string | null = null;
for (let attempt = 0; attempt < 10 && !wsUrl; attempt++) {
wsUrl = await window.electron.getAcpUrl();
if (!wsUrl) await new Promise((r) => setTimeout(r, 200));
}
if (!wsUrl) {
throw new Error('ACP URL is not available (goose backend not started or exited)');
} Defensive patterns
Strategy: retry
Validate before calling
// Wait until the main process has a backend URL for this window
async function waitForAcpUrl(timeoutMs = 5000): Promise<string> {
const deadline = Date.now() + timeoutMs;
for (;;) {
const url = await window.electron.getAcpUrl();
if (url) return url;
if (Date.now() > deadline) throw new Error('ACP backend URL not available before timeout');
await new Promise((r) => setTimeout(r, 200));
}
} Try / catch
try {
const client = await getAcpClient();
} catch (error) {
if (/ACP URL is not available/.test(String(error))) {
// Backend not up (yet) — surface a 'starting backend' state and retry on next app event
setBackendState('starting');
scheduleReconnect();
return;
}
throw error;
} Prevention
- Do not call getAcpClient until the main process signals the serve lease is ready.
- Watch main-process logs for early goose-serve exits so 'not available' is diagnosed as a crash, not a race.
- In external-backend mode, validate and persist the URL setting before enabling the mode.
When it happens
Trigger: Calling getAcpClient()/openConnection() before the main process has finished spawning 'goose serve' and recording the lease; after the serve process crashed or exited (gooseServeLeases.getAcpUrl then throws or returns null for that windowId); running with external-backend mode enabled but the URL setting missing.
Common situations: Startup race where the renderer connects faster than the backend binds its port; goose binary exits immediately (bad config, missing API key file) so the lease is torn down; window recreated with a new id that has no lease; development against an external goose daemon without setting the backend URL.
Related errors
- Cannot update message because the active prompt could not be
- GOOSE_SERVER__SECRET_KEY is required for goose serve
- goose serve did not become ready on ${statusUrl}.${exitDetai
- This window's Goose backend stopped. Close this window and o
- Failed to read the selected file.
AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16).
Data as JSON: /api/errors/0635a5fe5b47962a.
Report an issue: GitHub.