{"record":{"id":"16041b06bf916e9e","repo":"thedotmack/claude-mem","slug":"spawn-lock-holder-s-worker-not-ready-within-window","errorCode":null,"errorMessage":"Spawn-lock holder's worker not ready within window","messagePattern":"Spawn-lock holder's worker not ready within window","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/services/worker-spawner.ts","lineNumber":172,"sourceCode":"      }\n    } else {\n      logger.info('SYSTEM', 'Another launcher holds the spawn lock — skipping duplicate spawn and waiting for its worker');\n    }\n\n    const ready = await waitForReadiness(port, getPlatformTimeout(HOOK_TIMEOUTS.READINESS_WAIT));\n    if (!ready) {\n      const workerStillHealthy = await waitForHealth(port, 1000);\n      const workerPidStillAlive = cleanStalePidFile() === 'alive';\n      const spawnedProcessStillAlive = spawnedPid !== undefined && spawnedPid > 0 && isPidAlive(spawnedPid);\n      if (!workerStillHealthy && !workerPidStillAlive && !spawnedProcessStillAlive) {\n        logger.error('SYSTEM', spawnLockHeld\n          ? 'Worker exited before readiness endpoint became available'\n          : 'Spawn-lock holder never produced a live worker before readiness timed out');\n        return 'dead';\n      }\n      logger.warn('SYSTEM', spawnLockHeld\n        ? 'Worker spawned but readiness endpoint not responding within window'\n        : 'Spawn-lock holder\\'s worker not ready within window');\n      return 'warming';\n    }\n    clearWorkerSpawnAttempted();\n    // touchPidFile is existsSync-guarded and merely refreshes the live worker's\n    // pid-file mtime — correct for lock losers too, since the worker IS up.\n    touchPidFile();\n    logger.info('SYSTEM', spawnLockHeld\n      ? 'Worker started successfully'\n      : 'Worker is up (started by another launcher)');\n    return 'ready';\n  } finally {\n    if (spawnLockHeld) releaseSpawnLock();\n  }\n}\n","sourceCodeStart":154,"sourceCodeEnd":187,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/e2d1df569a8f04075d40e92461128ece7cf04c82/src/services/worker-spawner.ts#L154-L187","documentation":"This launcher lost the spawn gate (another hook, the MCP server, or the CLI already holds it), skipped its own spawn, and waited on the lock holder's worker. That worker did not pass /readiness within the window, but some sign of life (health endpoint, PID file, process) remains, so the path returns 'warming' with this warning rather than declaring failure.","triggerScenarios":"Two hooks fire simultaneously on a machine with no worker: one wins the spawn lock, the other waits; the winner's worker is still initializing when the loser's readiness wait expires.","commonSituations":"Parallel tool invocations at session start; MCP server and a hook both lazy-spawning; slow init making every waiter's window expire.","solutions":["Nothing to fix in the loser path — poll /readiness and the worker typically turns ready shortly","If it never turns ready, debug the winner's spawn: check worker logs for startup failures","Avoid racing launchers by starting the worker once at session/boot time before hooks fire"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"async function waitForHolderWorker(port: number, timeoutMs = 60000): Promise<boolean> {\n  const deadline = Date.now() + timeoutMs;\n  while (Date.now() < deadline) {\n    try { if (await fetch(`http://127.0.0.1:${port}/readiness`).then(r => r.ok)) return true; } catch {}\n    await new Promise(r => setTimeout(r, 1000));\n  }\n  return false;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never race launchers — start the worker once up front so hooks and MCP only find it running","When you lost the spawn lock, wait on the holder's worker instead of spawning a competitor","A single warm-up wait at session start removes the whole class of lock-loser waits"],"tags":["worker","spawn-lock","readiness","concurrency"],"backgroundTag":"service-not-ready","analyzedSha":"e2d1df569a8f04075d40e92461128ece7cf04c82","analyzedAt":"2026-08-20T23:58:13.836Z","contentChangedAt":"2026-08-20T23:58:13.836Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}