{"record":{"id":"2f4a96eb2feda70d","repo":"NousResearch/hermes-agent","slug":"hermes-backend-start-was-superseded-by-a-newer-con","errorCode":null,"errorMessage":"Hermes backend start was superseded by a newer connection attempt.","messagePattern":"Hermes backend start was superseded by a newer connection attempt\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"apps/desktop/electron/main.ts","lineNumber":8520,"sourceCode":"          HERMES_DESKTOP: '1',\n          // Our PID so the backend's parent-death watchdog self-exits if we die\n          // uncleanly (crash / SIGKILL / update handoff) instead of leaking a\n          // serving backend + its MCP child subtree. See web_server.py\n          // _start_parent_death_watchdog.\n          HERMES_PARENT_PID: String(process.pid),\n          HERMES_WEB_DIST: webDist,\n          ...(readyFile ? { HERMES_DESKTOP_READY_FILE: readyFile } : {})\n        },\n        shell: backend.shell,\n        stdio: ['ignore', 'pipe', 'pipe']\n      })\n    )\n\n    const processOwner = backendConnectionState.attachProcess(connectionAttempt, hermesProcess)\n\n    if (!processOwner) {\n      stopBackendChild(hermesProcess)\n      throw new Error('Hermes backend start was superseded by a newer connection attempt.')\n    }\n\n    hermesProcess.stdout.on('data', rememberLog)\n    hermesProcess.stderr.on('data', rememberLog)\n    let backendReady = false\n    let rejectBackendStart = null\n\n    const backendStartFailed = new Promise((_resolve, reject) => {\n      rejectBackendStart = reject\n    })\n\n    hermesProcess.once('error', error => {\n      if (!backendConnectionState.clearForCurrentProcess(processOwner)) {\n        rememberLog(`Ignoring stale Hermes backend error: ${error.message}`)\n        rejectBackendStart?.(new Error('Hermes backend start was superseded by a newer connection attempt.'))\n\n        return\n      }","sourceCodeStart":8502,"sourceCodeEnd":8538,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/apps/desktop/electron/main.ts#L8502-L8538","documentation":"Thrown when a freshly spawned Hermes backend child process loses a race: backendConnectionState.attachProcess(connectionAttempt, hermesProcess) returned falsy, meaning a newer connection attempt superseded this one while the spawn was in flight. The child is stopped (stopBackendChild) and the older attempt aborts rather than leaking a process attached to a dead attempt. This is a deliberate last-writer-wins guard, so it indicates rapid sequential reconnects, not a broken backend.","triggerScenarios":"User (or renderer logic) triggers connect/disconnect/connect quickly; a failed connection attempt auto-retries while the user manually reconnects; two profiles or windows race to boot backends in the same main process. attachProcess is called after spawn resolves, by which time connectionAttempt is no longer the current attempt.","commonSituations":"Clicking 'reconnect' repeatedly in the desktop UI during a slow backend boot; an auto-retry loop stacked on top of user-initiated connects; dev hot-restarts of the renderer re-issuing the boot IPC.","solutions":["Let the in-flight connection attempt finish (or explicitly disconnect) before starting another — avoid rapid reconnect clicks","If it appears during normal single-click use, check for a renderer-side retry loop issuing duplicate boot requests and debounce it","Verify the process is actually cleaned up (no orphan 'hermes serve' child) after this error, then connect once cleanly","Treat this error as retryable in UI: surface 'connection attempt was replaced' rather than a hard failure"],"exampleFix":"// before (renderer)\nonClick={() => void window.hermes.bootBackend(profile)} // fires on every click\n\n// after\nconst booting = useRef(false)\nonClick={() => { if (booting.current) return; booting.current = true; void window.hermes.bootBackend(profile).finally(() => { booting.current = false }) }}","handlingStrategy":"try-catch","validationCode":"// Serialize connection attempts in the renderer\nlet bootInFlight: Promise<BootResult> | null = null\nfunction boot(profile: string) {\n  bootInFlight ??= ipc.invoke('hermes:boot', profile).finally(() => { bootInFlight = null })\n  return bootInFlight\n}","typeGuard":"function isSupersededAttempt(e: unknown): boolean {\n  return e instanceof Error && e.message === 'Hermes backend start was superseded by a newer connection attempt.'\n}","tryCatchPattern":"try { await bootBackend(profile) } catch (e) { if (isSupersededAttempt(e)) { /* benign race: a newer attempt owns the boot; do nothing */ return } throw e }","preventionTips":["Debounce/guard the reconnect button while a boot is in flight","Do not stack auto-retry loops on top of user-initiated connects","Treat this exact message as a no-op in error reporting, not a backend failure"],"tags":["race-condition","process-management","desktop","startup","lifecycle"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}