thedotmack/claude-mem · warning
Worker unavailable, skipping hook
Error message
Worker unavailable, skipping hook: ${error instanceof Error ? error.message : error} What it means
This warning is logged by the claude-mem hook command when the background worker that processes hook events is unreachable. Rather than failing the hook (which would surface errors in Claude Code and could accumulate Windows Terminal tabs), the hook logs a warning, emits a no-op result, and exits 0. A separate fail-loud counter (recordWorkerUnreachable) tracks consecutive failures and surfaces the error after a threshold is reached.
Solutions
- Restart the claude-mem worker (the plugin auto-starts it; re-run the hook or reload Claude Code).
- Check the worker health endpoint / port configuration for a stale or wrong port.
- Reinstall or rebuild the plugin with `npm run build-and-sync` to reset worker state.
- If it persists, inspect worker logs at ~/.claude-mem/ for the underlying startup failure.
- Treat exit 0 as by-design: the fail-loud counter will emit hook_failed telemetry after N consecutive failures.
Defensive patterns
Strategy: fallback
Validate before calling
// before firing the hook, check worker reachability
const health = await fetch(`http://${host}:${port}/health`, { signal: AbortSignal.timeout(2000) }).then(r => r.ok).catch(() => false);
if (!health) console.warn('[hook] worker not reachable; hook will be skipped'); Type guard
function isWorkerUnavailableError(e: unknown): boolean {
return e instanceof Error && /ECONNREFUSED|ECONNRESET|ETIMEDOUT|ENOTFOUND|fetch failed/i.test(e.message);
} Try / catch
try {
await sendToWorker(event);
} catch (error) {
if (isWorkerUnavailableError(error)) {
logger.warn('HOOK', `Worker unavailable, skipping: ${error instanceof Error ? error.message : error}`);
exitGraceful(options); // exit 0 by design
return;
}
throw error;
} Prevention
- Ensure the worker is started before hooks run (session-start hook or plugin activation).
- Monitor the fail-loud counter; after N failures investigate worker logs in ~/.claude-mem/.
- Pin a valid worker port in config and avoid stale-port reuse.
- Add a health-check before dispatching time-sensitive hook events.
When it happens
Trigger: A hook event fires (session start, prompt submit, etc.) and the request to the worker process fails with an error classified by isWorkerUnavailableError — e.g. the worker process is not running, its port is closed, or the connection is refused.
Common situations: The claude-mem worker crashed or was killed; the plugin was installed but the worker never started; a stale worker port config; system reboot without the worker restarting; antivirus blocking local IPC.
Understand the failure class
Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.
Related errors
- Could not persist SessionEnd request for worker recovery
- Adapter rejected input
- Cannot lazy-spawn worker: worker-service.cjs not found in…
- Claude CLI preflight failed
- [claude-mem] Worker GET
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/d6df33e73539c7a5.
Report an issue: GitHub.
Appendix: source
Thrown at src/cli/hook-command.ts:147
const handler = getEventHandler(event);
try {
return await executeHookPipeline(adapter, handler, platform, options);
} catch (error) {
if (error instanceof AdapterRejectedInput) {
logger.warn('HOOK', `Adapter rejected input (${error.reason}), skipping hook`);
emitModelContext(adapter, buildNoOpResult(event));
exitGraceful(options);
return HOOK_EXIT_CODES.SUCCESS;
}
if (isNonBlockingHookInputError(error)) {
logger.warn('HOOK', `Hook input unavailable, skipping hook: ${error instanceof Error ? error.message : error}`);
emitModelContext(adapter, buildNoOpResult(event));
exitGraceful(options);
return HOOK_EXIT_CODES.SUCCESS;
}
if (isWorkerUnavailableError(error)) {
logger.warn('HOOK', `Worker unavailable, skipping hook: ${error instanceof Error ? error.message : error}`);
// EXIT_SIGNAL per CLAUDE.md: transient worker errors exit 0 to avoid
// Windows Terminal tab accumulation. The fail-loud counter (worker-utils
// recordWorkerUnreachable) handles the surface-after-N-failures path and
// emits the threshold-gated hook_failed telemetry internally. Awaited:
// when the count JUST reaches the threshold it sends the event and then
// exits 2; exitGraceful below would kill a pending POST mid-flight.
await recordWorkerUnreachable();
exitGraceful(options);
return HOOK_EXIT_CODES.SUCCESS;
}
logger.error('HOOK', `Hook error: ${error instanceof Error ? error.message : error}`, {}, error instanceof Error ? error : undefined);
// hook_failed telemetry MUST be awaited BEFORE emitBlockingError — it
// calls process.exit(2), which would kill a fire-and-forget POST
// mid-flight. captureCliEvent never throws and is hard-capped at 2s.
// Closed-enum props only: the error message itself is never sent.
{
const hookType = getActiveHookType();View on GitHub (pinned to d8bc9755e7)