{"record":{"id":"4b24c09a3b077006","repo":"thedotmack/claude-mem","slug":"worker-lazy-spawned-but-did-not-become-ready-befor","errorCode":null,"errorMessage":"Worker lazy-spawned but did not become ready before hook readiness timeout","messagePattern":"Worker lazy-spawned but did not become ready before hook readiness timeout","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/shared/worker-utils.ts","lineNumber":604,"sourceCode":"    // `start` hook is booting the daemon in parallel, and a cold macOS+Chroma\n    // worker needs ~7s to bind. The old 3-attempt/250ms budget (~0.75s) expired\n    // long before that, so the context (and session-init) hooks raced boot and\n    // soft-failed to empty — dropping memory injection and the user_prompts row\n    // (the upstream trigger for #2794). Wait up to ~15.5s (≈ POST_SPAWN_WAIT) so\n    // whichever worker wins the port is seen before we give up.\n    const alive = await waitForWorkerPort({ attempts: 6, backoffMs: 500 });\n    if (!alive) {\n      logger.warn('SYSTEM', spawnLockHeld\n        ? 'Worker port did not open after lazy-spawn within the cold-boot wait (~15s)'\n        : 'Spawn-lock holder\\'s worker port did not open within the cold-boot wait (~15s)');\n      return false;\n    }\n  } finally {\n    if (spawnLockHeld) releaseSpawnLock();\n  }\n  const ready = await waitForWorkerReadiness();\n  if (!ready) {\n    logger.warn('SYSTEM', 'Worker lazy-spawned but did not become ready before hook readiness timeout');\n    return false;\n  }\n  // Amplifier guard: even if the worker that won the port is still stale,\n  // never recycle a second time in the same hook invocation.\n  if (expectedPluginVersion !== null) {\n    await warnIfVersionStillMismatched(expectedPluginVersion);\n  }\n  return true;\n}\n\nlet aliveCache: boolean | null = null;\n\nexport async function ensureWorkerAliveOnce(): Promise<boolean> {\n  if (aliveCache !== null) return aliveCache;\n  aliveCache = await ensureWorkerRunning();\n  return aliveCache;\n}\n","sourceCodeStart":586,"sourceCodeEnd":622,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/e2d1df569a8f04075d40e92461128ece7cf04c82/src/shared/worker-utils.ts#L586-L622","documentation":"The worker's port opened (it is alive) but waitForWorkerReadiness timed out before the ready flag flipped — readiness additionally requires the health endpoint to report the worker fully initialized. The hook returns false and skips its API call, degrading that hook event.","triggerScenarios":"Worker bound its port but is still initializing (Chroma connect, embeddings setup, DB migrations, session store warm-up) past the readiness budget.","commonSituations":"First-run Chroma embedding model download; a large migration after upgrade; slow or busy disks stretching init past the timeout.","solutions":["Wait and retry — the next hook event usually finds the worker ready; send the next prompt.","Raise the readiness budget via CLAUDE_MEM_HEALTH_TIMEOUT_MS or the corresponding settings key (bounded; see readTimeoutEnv).","Inspect worker logs for the long-running init step and fix that (e.g., pre-download embedding models).","Pre-warm the worker at login so sessions never race initialization."],"exampleFix":"# before\nexport CLAUDE_MEM_HEALTH_TIMEOUT_MS=2000   # too short for cold Chroma boot\n\n# after\nexport CLAUDE_MEM_HEALTH_TIMEOUT_MS=20000  # within the 500..300000 bounds","handlingStrategy":"retry","validationCode":"const ready = await waitForWorkerReadiness();\nif (!ready) {\n  // port is open but init incomplete — skip this event, retry next\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":["Budget the readiness timeout for your worst cold boot, not your average one.","Pre-warm the worker at login; first-run Chroma downloads are the usual slow step.","After a claude-mem upgrade, open one warm-up session before relying on memory injection."],"tags":["worker","readiness","timeout","cold-boot","claude-mem"],"backgroundTag":"readiness-check-timeout","analyzedSha":"e2d1df569a8f04075d40e92461128ece7cf04c82","analyzedAt":"2026-08-20T23:58:13.836Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}