aaif-goose/goose · error

goose serve TLS certificate fingerprint did not match readin

Error message

goose serve TLS certificate fingerprint did not match readiness probe

What it means

main.ts pins the server certificate: it keeps the fingerprint observed during the readiness probe (localCertificateTrust.trust.fingerprint) and compares it with the fingerprint goose serve itself reported. A mismatch means the endpoint's TLS identity changed between probe and serve — possible cert regeneration race, stale trust record from a previous run, or something else terminating TLS. The serve process is cleaned up and startup aborts with the 'goose serve failed to start' dialog.

Source

Thrown at ui/desktop/src/main.ts:1232

        resourcesPath: app.isPackaged ? process.resourcesPath : undefined,
        logger: log,
        diagnosticsDir: STARTUP_LOGS_DIR,
        readinessFetch: net.fetch as unknown as typeof globalThis.fetch,
      });
      if (!gooseServeResult.certFingerprint) {
        await gooseServeResult.cleanup();
        throw new Error(
          'goose serve started with TLS but did not return a certificate fingerprint'
        );
      }

      const localCertFingerprint = normalizeFingerprint(gooseServeResult.certFingerprint);
      if (
        localCertificateTrust.trust.fingerprint &&
        localCertificateTrust.trust.fingerprint !== localCertFingerprint
      ) {
        await gooseServeResult.cleanup();
        throw new Error('goose serve TLS certificate fingerprint did not match readiness probe');
      }
      localCertificateTrust.trust.fingerprint = localCertFingerprint;
    } catch (error) {
      localCertificateTrust.release();
      log.error('goose serve failed to start', error);
      dialog.showMessageBoxSync({
        type: 'error',
        title: 'Goose Failed to Start',
        message: 'The backend server failed to start.',
        detail: [
          'Backend: goose serve',
          'Readiness check: HTTPS GET /status',
          `Startup error:\n${errorMessage(error)}`,
        ].join('\n\n'),
        buttons: ['OK'],
      });
      app.quit();
      return;

View on GitHub (pinned to 3810898a74)

Solutions

  1. Restart the app so the trust record is refreshed with the new cert, and clear any persisted stale fingerprint if it persists across restarts
  2. Verify only one goose serve is running and the probe and serve hit the same process/port
  3. Disable TLS interception (proxies, corporate middleboxes) for 127.0.0.1 backend traffic
  4. Keep desktop and goose binary versions in lockstep so cert generation/pinning logic matches
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await startServeWithTrust();
} catch (e) {
  if (e instanceof Error && e.message.includes('fingerprint did not match')) {
    // clear persisted trust state and prompt restart; a retry with stale pinning will fail again
  }
  throw e;
}

Prevention

When it happens

Trigger: A cached trust fingerprint from an earlier session while the server regenerated its self-signed cert this run; port reuse where a different process answered the probe; proxy/MITM terminating TLS so probe and serve present different certs; clock/entropy issues causing new certs per start with stale pinning state.

Common situations: Restarting Goose Desktop after the server cert file was deleted/regenerated; tests that run multiple serve instances against shared state; corporate TLS inspection inserting its own cert on one of the two paths.

Understand the failure class

Related errors


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