{"record":{"id":"d757c23e941c8e8f","repo":"thedotmack/claude-mem","slug":"worker-is-alive-but-readiness-timed-out-proceedi","errorCode":null,"errorMessage":"Worker is alive but readiness timed out — proceeding anyway","messagePattern":"Worker is alive but readiness timed out — proceeding anyway","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/services/worker-spawner.ts","lineNumber":112,"sourceCode":"      clearWorkerSpawnAttempted();\n      logger.info('SYSTEM', 'Worker became ready while waiting on live PID');\n      return 'ready';\n    }\n    const workerStillHealthy = await waitForHealth(port, 1000);\n    const workerPidStillAlive = cleanStalePidFile() === 'alive';\n    if (!workerStillHealthy && !workerPidStillAlive) {\n      logger.error('SYSTEM', 'Live PID disappeared before readiness endpoint became available');\n      return 'dead';\n    }\n    logger.warn('SYSTEM', 'Live PID detected but worker did not become ready before timeout');\n    return 'warming';\n  }\n\n  if (await waitForHealth(port, 1000)) {\n    clearWorkerSpawnAttempted();\n    const ready = await waitForReadiness(port, getPlatformTimeout(HOOK_TIMEOUTS.READINESS_WAIT));\n    if (!ready) {\n      logger.warn('SYSTEM', 'Worker is alive but readiness timed out — proceeding anyway');\n    }\n    logger.info('SYSTEM', 'Worker already running and healthy');\n    return ready ? 'ready' : 'warming';\n  }\n\n  const portInUse = await isPortInUse(port);\n  if (portInUse) {\n    logger.info('SYSTEM', 'Port in use, waiting for worker to become healthy');\n    const healthy = await waitForHealth(port, getPlatformTimeout(HOOK_TIMEOUTS.PORT_IN_USE_WAIT));\n    if (healthy) {\n      clearWorkerSpawnAttempted();\n      const ready = await waitForReadiness(port, getPlatformTimeout(HOOK_TIMEOUTS.READINESS_WAIT));\n      logger.info('SYSTEM', 'Worker is now healthy');\n      return ready ? 'ready' : 'warming';\n    }\n    logger.error('SYSTEM', 'Port in use but worker not responding to health checks');\n    return 'dead';\n  }","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/e2d1df569a8f04075d40e92461128ece7cf04c82/src/services/worker-spawner.ts#L94-L130","documentation":"The spawner found the worker already running and healthy (/health responds), but /readiness did not pass within the wait window. It logs this warning, returns 'warming', and proceeds — the worker serves basic health while DB/search initialization is still finishing in the background.","triggerScenarios":"Calling a hook or MCP tool during the init tail: health endpoint is up (server listening) but the search routes are not registered yet.","commonSituations":"Hooks firing immediately after `claude-mem start`; automation that polls /health and assumes full readiness; slow disks delaying index load.","solutions":["Switch readiness checks from /health to /readiness before invoking memory-dependent features","Retry the memory-dependent call after a short wait — 'warming' transitions to ready without intervention","If permanently warming, inspect logs for init errors (dependency preflight, Chroma startup)"],"exampleFix":"// before\nconst ok = await fetch(`${base}/health`).then(r => r.ok);\n\n// after\nconst ok = await fetch(`${base}/readiness`).then(r => r.ok);","handlingStrategy":"retry","validationCode":"async function isReady(base: string): Promise<boolean> {\n  try { return await fetch(`${base}/readiness`).then(r => r.ok); } catch { return false; }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use /readiness for feature gating; /health only proves the process is alive","Retry memory-dependent calls once after 'warming' — init usually completes on its own","Avoid inferring full availability from a successful spawn or health check"],"tags":["worker","readiness","health-check","startup"],"backgroundTag":"service-not-ready","analyzedSha":"e2d1df569a8f04075d40e92461128ece7cf04c82","analyzedAt":"2026-08-20T23:58:13.836Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}