thedotmack/claude-mem · warning

Worker unavailable on Windows — skipping spawn (recent attem

Error message

Worker unavailable on Windows — skipping spawn (recent attempt failed within cooldown)

What it means

On Windows, after a failed spawn attempt claude-mem writes a cooldown marker file; shouldSkipSpawnOnWindows() suppresses further spawn attempts while the marker is younger than 2 minutes (WINDOWS_SPAWN_COOLDOWN_MS). This warning fires instead of spawning and the caller gets 'dead', preventing a hook-driven spawn storm on broken setups.

Source

Thrown at src/services/worker-spawner.ts:133

    return ready ? 'ready' : 'warming';
  }

  const portInUse = await isPortInUse(port);
  if (portInUse) {
    logger.info('SYSTEM', 'Port in use, waiting for worker to become healthy');
    const healthy = await waitForHealth(port, getPlatformTimeout(HOOK_TIMEOUTS.PORT_IN_USE_WAIT));
    if (healthy) {
      clearWorkerSpawnAttempted();
      const ready = await waitForReadiness(port, getPlatformTimeout(HOOK_TIMEOUTS.READINESS_WAIT));
      logger.info('SYSTEM', 'Worker is now healthy');
      return ready ? 'ready' : 'warming';
    }
    logger.error('SYSTEM', 'Port in use but worker not responding to health checks');
    return 'dead';
  }

  if (shouldSkipSpawnOnWindows()) {
    logger.warn('SYSTEM', 'Worker unavailable on Windows — skipping spawn (recent attempt failed within cooldown)');
    return 'dead';
  }

  // Spawn gate (src/shared/worker-spawn-gate.ts): only ONE gated launcher —
  // hook, MCP server, or the CLI restart fallback — may spawn at a time. (The
  // dying worker's restart handoff in worker-shutdown.ts is deliberately NOT
  // gated: it is the primary spawner on restart, and hooks wait for its
  // successor.) Losing the lock never fails this path; the loser skips its
  // spawn and waits for the holder's worker. The winner holds the lock through
  // the readiness wait and releases it in finally on every exit path.
  const spawnLockHeld = acquireSpawnLock();
  let spawnedPid: number | undefined;
  try {
    if (spawnLockHeld) {
      logger.info('SYSTEM', 'Starting worker daemon', { workerScriptPath });
      markWorkerSpawnAttempted();
      spawnedPid = spawnDaemon(workerScriptPath, port);
      if (spawnedPid === undefined) {

View on GitHub (pinned to e2d1df569a)

Solutions

  1. Check the worker log for the original spawn failure and fix it (install runtime, whitelist the daemon, fix directory permissions)
  2. Wait at least 2 minutes for the cooldown to lapse, then retry — the marker self-heals
  3. If certain the root cause is fixed, delete the cooldown marker file to retry immediately
Defensive patterns

Strategy: retry

Validate before calling

const WINDOWS_SPAWN_COOLDOWN_MS = 2 * 60 * 1000;

function cooldownRemaining(markerPath: string): number {
  try {
    const mtime = fs.statSync(markerPath).mtimeMs;
    return Math.max(0, WINDOWS_SPAWN_COOLDOWN_MS - (Date.now() - mtime));
  } catch { return 0; }
}

Prevention

When it happens

Trigger: Any hook-triggered lazy spawn within 120 seconds of a previous spawn failure on Windows; the marker file's mtime is recent and no healthy worker ever appeared.

Common situations: Antivirus or Defender blocking the daemon executable; missing node/bun runtime; permission errors on the log or data directory; the marker left behind by an earlier unrelated failure that was since fixed.

Related errors


AI-assisted analysis of thedotmack/claude-mem@e2d1df569a (2026-08-20). Data as JSON: /api/errors/dce40710ae11e267. Report an issue: GitHub.