thedotmack/claude-mem · error · Error
Worker already running
Error message
Worker already running
What it means
The supervisor refuses to start when a worker PID file indicates another worker process is already alive. validateWorkerPidFile checks whether the PID recorded on disk belongs to a live process; 'alive' means a previous instance is still running. This is a single-instance guard to prevent two workers from fighting over the same resources.
Solutions
- Stop the already-running worker process (find it via the PID in the worker PID file) and start again
- Remove the stale PID file only after confirming the process is dead, then retry
- Use a process manager (systemd/launchd) to ensure only one worker instance runs
Example fix
// before
supervisor.start(); // throws if pidfile is alive
// after
if (validateWorkerPidFile({ logAlive: false }) === 'alive') {
await stopExistingWorker();
}
supervisor.start(); Defensive patterns
Strategy: try-catch
Validate before calling
import { validateWorkerPidFile } from './pidfile';
const canStart = validateWorkerPidFile({ logAlive: false }) !== 'alive'; Try / catch
try { await supervisor.start(); }
catch (e) { if (e.message === 'Worker already running') { console.error('worker pid:', readPidFile()); process.exit(1); } throw e; } Prevention
- Run the worker under a single-instance process manager
- Always shut down via the supervisor's stop path so the PID file is cleaned
- Check the PID file before attempting manual restarts
When it happens
Trigger: Calling supervisor.start() when a previous worker instance is still running and its PID file points to a live process.
Common situations: Starting a second instance of the app while the first still runs; a stale-but-alive worker left over after a crashed parent; running dev and production instances against the same data directory.
Related errors
- Failed to spawn SDK subprocess for session
- Hard cap exceeded: processes in registry (cap= ). Refusing…
- installPluginDependencies: no package.json at
- Supervisor is shutting down, refusing to spawn
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/9cdb8695d8eaf87a.
Report an issue: GitHub.
Appendix: source
Thrown at src/supervisor/index.ts:42
class Supervisor {
private readonly registry: ProcessRegistry;
private started = false;
private stopPromise: Promise<void> | null = null;
private signalHandlersRegistered = false;
private shutdownInitiated = false;
private shutdownHandler: (() => Promise<void>) | null = null;
constructor(registry: ProcessRegistry) {
this.registry = registry;
}
async start(): Promise<void> {
if (this.started) return;
this.registry.initialize();
const pidStatus = validateWorkerPidFile({ logAlive: false });
if (pidStatus === 'alive') {
throw new Error('Worker already running');
}
this.started = true;
// Reap chroma-mcp trees that no worker owns (#3905). Detached and best-effort: the sweep reads
// the process table, so it must never gate boot, and a failure leaves the pre-sweep state.
// It runs here, after initialize() and before anything of ours is spawned, so every signature
// tree in the table with a dead or PID-1 parent is by construction someone else's leftover.
void sweepOrphanedChromaTrees({ registry: this.registry }).catch((error: unknown) => {
logger.warn('PROCESS', 'Orphaned chroma-mcp sweep failed', {
error: error instanceof Error ? error.message : String(error),
});
});
startHealthChecker();
}
configureSignalHandlers(shutdownHandler: () => Promise<void>): void {View on GitHub (pinned to d8bc9755e7)