{"record":{"id":"d0184e7f256c82e9","repo":"thedotmack/claude-mem","slug":"worker-is-healthy-but-not-ready-skipping-hook-api","errorCode":null,"errorMessage":"Worker is healthy but not ready; skipping hook API call","messagePattern":"Worker is healthy but not ready; skipping hook API call","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/shared/worker-utils.ts","lineNumber":482,"sourceCode":"  // (warnIfVersionStillMismatched). Stays null when no worker was alive\n  // (plain cold-start lazy-spawn — no recycle happened, nothing to amplify)\n  // or when the resolved version is unreadable ('unknown').\n  let expectedPluginVersion: string | null = null;\n\n  if (await isWorkerPortAlive()) {\n    // A worker is already alive. If it is a DIFFERENT version than the one\n    // this resolution would spawn (e.g. the user upgraded but the previous\n    // worker is still squatting the port), recycle it so the resolved\n    // version takes over — otherwise the stale worker keeps serving\n    // indefinitely.\n    const { matches, pluginVersion, workerVersion } = await checkVersionMatch(getWorkerPort(), resolvedScript?.version ?? null);\n    if (pluginVersion !== 'unknown') {\n      expectedPluginVersion = pluginVersion;\n    }\n    if (matches) {\n      const ready = await waitForWorkerReadiness();\n      if (!ready) {\n        logger.warn('SYSTEM', 'Worker is healthy but not ready; skipping hook API call');\n        return false;\n      }\n      if (expectedPluginVersion !== null) {\n        await warnIfVersionStillMismatched(expectedPluginVersion);\n      }\n      return true;\n    }\n\n    logger.info('SYSTEM', 'Worker version mismatch — killing stale worker', {\n      pluginVersion,\n      workerVersion,\n    });\n    // The stale worker must never run its own replacement. The previous\n    // design (POST /api/admin/restart, then the dying worker spawns its\n    // successor) executed the OLD install's handoff code: a ≤13.11.0 worker\n    // resolves the successor script from its own install dir, respawns its\n    // own version, and re-binds the port before this hook's lazy-spawn — so\n    // the mismatch recurs on every hook forever (#3378: 2,424 recycles in","sourceCodeStart":464,"sourceCodeEnd":500,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/e2d1df569a8f04075d40e92461128ece7cf04c82/src/shared/worker-utils.ts#L464-L500","documentation":"A worker is already alive on the port with a matching version, but waitForWorkerReadiness timed out before the worker flipped its ready flag (readiness is stronger than port-open/health-alive). The hook skips its API call and returns false, so that hook event degrades (e.g., no memory injected).","triggerScenarios":"Cold boot where the port binds quickly but internal init (Chroma, DB migrations, session store) exceeds the readiness budget; waitForWorkerReadiness exhausts its attempts right before the ready flag sets.","commonSituations":"First session after a reboot (cold macOS+Chroma start needs ~7s); slow disks; CPU contention from a busy machine slowing worker init.","solutions":["Retry by invoking another hook event / sending the next prompt — the worker usually finishes booting on its own.","Raise the health/readiness budget via CLAUDE_MEM_HEALTH_TIMEOUT_MS (bounded; see readTimeoutEnv).","Pre-warm the daemon at login so the first session does not race boot.","Check worker logs for the init step that is slow (Chroma first-run, migrations) and address it."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"const ready = await waitForWorkerReadiness();\nif (!ready) {\n  // worker still initializing — skip API call this event, retry next event\n  return false;\n}","typeGuard":"const isWorkerReady = async (): Promise<boolean> => {\n  try { return (await fetchWorkerHealth()).ready === true; }\n  catch { return false; }\n};","tryCatchPattern":null,"preventionTips":["Pre-warm the worker daemon at login so the first session never races readiness.","Size CLAUDE_MEM_HEALTH_TIMEOUT_MS for your slowest cold boot (default budgets assume ~7s Chroma start).","Treat healthy-but-not-ready as transient: retry on the next hook event before debugging."],"tags":["worker","readiness","cold-boot","timeout","claude-mem"],"backgroundTag":"service-not-ready","analyzedSha":"e2d1df569a8f04075d40e92461128ece7cf04c82","analyzedAt":"2026-08-20T23:58:13.836Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}