aaif-goose/goose · error

goose serve started with TLS but did not return a certificat

Error message

goose serve started with TLS but did not return a certificate fingerprint

What it means

After startGooseServe succeeds with TLS enabled, main.ts expects the result to carry certFingerprint (the serve process's self-signed cert pin). A missing fingerprint despite a TLS-enabled start means the version/format contract between the readiness probe (which captured a fingerprint) and the serve result diverged — the process is killed via cleanup() and app startup fails with a dialog.

Source

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

    let gooseServeResult: Awaited<ReturnType<typeof startGooseServe>>;
    try {
      gooseServeResult = await startGooseServe({
        serverSecret,
        dir: workingDir,
        tls: true,
        env: {
          GOOSE_PATH_ROOT: appConfig.GOOSE_PATH_ROOT as string | undefined,
        },
        loginShellPath,
        isPackaged: app.isPackaged,
        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',

View on GitHub (pinned to 3810898a74)

Solutions

  1. Update the goose binary so its TLS fingerprint output matches the desktop app's parser (keep binary and app versions in lockstep)
  2. If GOOSE_BINARY is set in dev, unset it or rebuild it from the same checkout
  3. Check the startup diagnostics trace for fingerprint_wait events to see whether waitForFingerprint timed out vs never started
  4. As a workaround, run without TLS locally so the fingerprint contract is skipped
Defensive patterns

Strategy: try-catch

Try / catch

// main.ts already wraps this in try/catch with cleanup + dialog; callers should let it
// propagate and rely on that handler:
try {
  await startGooseServeFlow();
} catch (e) {
  if (e instanceof Error && e.message.includes('did not return a certificate fingerprint')) {
    // binary/app version skew: update goose, don't retry loop
  }
  throw e;
}

Prevention

When it happens

Trigger: goose binary emits the fingerprint on a channel gooseServe.ts no longer parses (or vice versa after a downgrade); TLS handshake succeeded but fingerprint extraction from stdout/file returned nothing; mixed old binary + new desktop build.

Common situations: Partial updates where the desktop app updates but the bundled/native goose binary stays old (or GOOSE_BINARY points at a stale build); dev running against a locally built goose with changed output format; snapshot tests with a fake readinessFetch that never returns a fingerprint.

Understand the failure class

Related errors


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