{"record":{"id":"10460978b1c8d1b9","repo":"thedotmack/claude-mem","slug":"worker-is-ready-but-still-reports-a-stale-version","errorCode":null,"errorMessage":"Worker is ready but still reports a stale version; not recycling again in this hook invocation (one recycle per hook event)","messagePattern":"Worker is ready but still reports a stale version; not recycling again in this hook invocation \\(one recycle per hook event\\)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/shared/worker-utils.ts","lineNumber":429,"sourceCode":"      await workerHttpRequest('/api/health', { timeoutMs: HEALTH_CHECK_TIMEOUT_MS });\n    } catch {\n      return true;\n    }\n    if (Date.now() - start >= timeoutMs) return false;\n    await new Promise<void>(resolve => setTimeout(resolve, 200));\n  }\n}\n\n/**\n * Amplifier guard: a hook recycles a stale worker AT MOST once per\n * invocation. If the worker that became ready still reports a mismatched\n * version, warn and return — the NEXT hook event retries. Recycling again in\n * the same invocation re-creates the restart storm.\n */\nasync function warnIfVersionStillMismatched(expectedPluginVersion: string): Promise<void> {\n  const observedVersion = await fetchWorkerHealthVersion();\n  if (observedVersion !== null && observedVersion !== expectedPluginVersion) {\n    logger.warn('SYSTEM', 'Worker is ready but still reports a stale version; not recycling again in this hook invocation (one recycle per hook event)', {\n      pluginVersion: expectedPluginVersion,\n      workerVersion: observedVersion,\n    });\n  }\n}\n\nasync function isWorkerPortAlive(): Promise<boolean> {\n  let healthy: boolean;\n  try {\n    healthy = await isWorkerHealthy();\n  } catch (error: unknown) {\n    logger.debug('SYSTEM', 'Worker health check threw', {\n      error: error instanceof Error ? error.message : String(error),\n    });\n    return false;\n  }\n  if (!healthy) return false;\n","sourceCodeStart":411,"sourceCodeEnd":447,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/8bc631a71a487424b866756e43a6efa4574cc66b/src/shared/worker-utils.ts#L411-L447","documentation":"Amplifier guard: a hook recycles a version-mismatched worker at most once per invocation. After readiness, warnIfVersionStillMismatched re-fetches the worker's health version; if it still differs from the plugin's version, this warning fires and the hook returns success anyway — recycling again in the same invocation would re-create a restart storm. The NEXT hook event retries the recycle.","triggerScenarios":"A stale worker was killed and respawned, but the worker that came up still reports a version different from expectedPluginVersion — e.g. resolution picked an old worker-service build, or version constants drifted between plugin and worker.","commonSituations":"Upgrade left two copies of the plugin/worker scripts on disk; symlinked marketplace install lagging the built one; partial build-and-sync.","solutions":["Trigger one more hook event (open a new Claude session) — the next invocation retries the recycle; often self-heals.","If it persists, use the logged pluginVersion vs workerVersion to locate duplicate installs and remove the stale one.","Run npm run build-and-sync to rebuild and re-sync the plugin so resolved script and version agree.","As a last resort, kill the worker manually so the next hook spawns a fresh one from the resolved script."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"const observed = await fetchWorkerHealthVersion();\nif (observed !== null && observed !== expectedPluginVersion) {\n  // defer to next hook event — do NOT recycle again now\n  return;\n}","typeGuard":"const isVersionMismatch = (observed: string | null, expected: string): boolean =>\n  observed !== null && observed !== expected;","tryCatchPattern":null,"preventionTips":["Run npm run build-and-sync after upgrades so plugin and worker builds stay in lockstep.","Remove old copies of the plugin/worker scripts so resolution can't pick a stale build.","Know that one stale-version warning right after upgrade is designed self-healing — the next hook retries."],"tags":["version-mismatch","worker-recycle","upgrade","claude-mem"],"backgroundTag":"stale-service-version","analyzedSha":"8bc631a71a487424b866756e43a6efa4574cc66b","analyzedAt":"2026-08-20T23:58:13.836Z","contentChangedAt":"2026-08-20T23:58:13.836Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}