aaif-goose/goose · error

goose serve did not become ready on ${statusUrl}.${exitDetai

Error message

goose serve did not become ready on ${statusUrl}.${exitDetails}${stderrDetails}

What it means

startGooseServe spawns the goose binary with `goose serve`, then polls a status URL (HTTPS GET /status by default) until ready. If the process never reports ready within the startup timeout, this error is thrown after cleanup; the message includes the statusUrl, whether the process exited (code and signal), and any collected stderr, plus the diagnostics trace path. It means the backend either crashed, hung, or is listening somewhere other than the probed URL.

Source

Thrown at ui/desktop/src/gooseServe.ts:558

    healthUrl,
    readinessFetch,
    onEvent: startupTrace?.record,
  });

  const stopOutputCollection = () => {
    stopStdoutCollection();
    gooseProcess.stderr?.off('data', onStderrData);
    gooseProcess.stderr?.resume();
  };

  if (!ready) {
    stopOutputCollection();
    await cleanup();
    const exitDetails = exited
      ? ` Process exited with code ${exitCode} and signal ${exitSignal}.`
      : '';
    const stderrDetails = errorLog.length ? ` Stderr: ${errorLog.join('\n')}` : '';
    throw new Error(
      withStartupDiagnosticsPath(
        `goose serve did not become ready on ${statusUrl}.${exitDetails}${stderrDetails}`,
        startupDiagnosticsPath
      )
    );
  }

  if (tls) {
    startupTrace?.record('fingerprint_wait_start', { timeoutMs: TLS_FINGERPRINT_TIMEOUT_MS });
    const fingerprint = await waitForFingerprint(fingerprintReady, TLS_FINGERPRINT_TIMEOUT_MS);
    if (!fingerprint) {
      stopOutputCollection();
      await cleanup();
      const exitDetails = exited
        ? ` Process exited with code ${exitCode} and signal ${exitSignal}.`
        : '';
      const stderrDetails = errorLog.length ? ` Stderr: ${errorLog.join('\n')}` : '';
      startupTrace?.record('fingerprint_missing', {

View on GitHub (pinned to 3810898a74)

Solutions

  1. Read the stderr text embedded in the message and the diagnostics file path appended to it — they usually name the real failure (e.g. 'unknown command serve', config parse error)
  2. Run the same command manually to reproduce: `<goosePath> serve` with the env shown in diagnostics, and watch it exit
  3. Kill stale goose processes and retry so findAvailablePort gets a clean port
  4. If the binary is outdated, update it (or point the build at the correct bundled binary) so `serve` exists and matches the desktop app version
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight the binary before starting:
import { access, constants } from 'fs/promises';
await access(goosePath, constants.X_OK); // throws early if binary missing

Try / catch

try {
  result = await startGooseServe(opts);
} catch (e) {
  const m = e instanceof Error ? e.message : '';
  const diagPath = m.match(/diagnostics?:?\s*(\S+)/)?.[1];
  // show m to user; open diagPath for full startup trace
  throw e;
}

Prevention

When it happens

Trigger: goose serve exits immediately (bad flag, unsupported subcommand in an old binary, port conflict after findAvailablePort race); the /status endpoint never returns ok within the readiness timeout; the process hangs on startup due to broken config or a locked GOOSE_PATH_ROOT.

Common situations: Bundled goose binary is older than the desktop app and lacks `serve`; leftover goose serve processes holding ports; corporate proxy intercepting readinessFetch; first-run config migration stalling startup; TLS env vars set inconsistently so the server listens with plain HTTP while the probe uses HTTPS.

Related errors


AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16). Data as JSON: /api/errors/4bb6abd18c900724. Report an issue: GitHub.