How to Troubleshoot Daemon Startup and Worker Crash Issues in Prime Agent
Start the daemon with --log-level debug, remove stale socket files at the path returned by getDaemonLogPath(), and query WorkerRecoveryJournal.readLatest() to isolate the exact crash stage.
Prime Agent orchestrates interactive AI coding sessions through a background daemon process that spawns and supervises worker processes handling model inference. When the daemon fails to start or workers crash during initialization, symptoms often manifest as immediate socket errors, repeated restarts, or silent hangs. This guide walks through the exact source files—such as daemon-mode.ts and daemon-supervisor.ts—and diagnostic commands you need to troubleshoot daemon startup or worker crash issues in Prime Agent.
Common Failure Patterns
The Prime Agent codebase handles three distinct failure modes:
- Daemon bind failure: The process exits immediately when
prepareDaemonSocketPath()cannot create the Unix-domain socket, usually due to invalid paths, permissions, or stale sockets still held by previous instances. Handled inpackages/coding-agent/src/modes/daemon/daemon-mode.ts. - Worker uncaught exception: The supervisor detects a runtime error in the worker, often from missing environment variables or model provider crashes. Logic resides in
packages/coding-agent/src/modes/daemon/daemon-supervisor.ts. - Repeated daemon restarts: Corrupted recovery state or stale journal entries trigger a crash loop during worker resurrection. Managed by
packages/coding-agent/src/modes/daemon/worker-recovery-journal.ts.
Diagnosing Daemon Startup Failures
Verify Socket Path and Permissions
The daemon initializes its Unix-domain socket via prepareDaemonSocketPath() (defined in daemon-socket.ts). If the socket file already exists, the daemon aborts with "socket already in use".
Check for stale sockets and remove them before starting:
rm -f /tmp/prime-agent.sock
./prime-agent.sh --mode daemon --daemon-socket /tmp/prime-agent.sock
If the path is invalid or permissions are insufficient, the log (see getDaemonLogPath() in packages/coding-agent/src/config.ts) will record the exact EACCES or ENOENT error.
Confirm Daemon Supervisor Ownership
The DaemonSupervisor class enforces single-owner semantics. If another instance holds ownership, assertDaemonSupervisorOwnerCurrent() logs "Another daemon supervisor already holds ownership" (around line 1200 in daemon-supervisor.ts) and refuses to spawn workers.
Verify ownership programmatically:
import { DaemonSupervisor } from "./daemon-supervisor.js";
const supervisor = new DaemonSupervisor("/tmp/prime-agent.sock");
if (!supervisor.isOwnerCurrent()) {
console.error("Another daemon instance holds ownership – aborting start.");
}
Terminate orphaned processes if necessary (see Cleanup Orphaned Processes).
Review Protocol Version Compatibility
A mismatch between the client and daemon capabilities causes handshake failures. The daemon advertises DAEMON_PROTOCOL_INFO and validates against DAEMON_SCHEMA_REVISION defined in daemon-protocol.ts.
If logs show "Unsupported protocol version", rebuild the client to align with the server:
npm run check
Debugging Worker Crashes
Read the Worker Recovery Journal
Workers persist crash metadata to a journal file located at <daemon-socket>.recovery. The WorkerRecoveryJournal class in worker-recovery-journal.ts provides readLatest() to parse these entries.
Inspect the journal to find the failing operation:
import { WorkerRecoveryJournal } from "./worker-recovery-journal.js";
const records = WorkerRecoveryJournal.readLatest("/tmp/prime-agent.sock.recovery");
records.forEach(r => console.log(`[${r.recordedAt}] ${r.operation} – busy=${r.busy}`));
Look for records where busy: true and operation indicates "init" or "modelLoad" to identify the crash stage.
Launch a Worker Manually
To isolate provider-specific errors, invoke a worker directly using the same spec the supervisor generates via createCliSubprocessLaunchSpec() in cli/subprocess-launch.js:
import { createCliSubprocessLaunchSpec } from "../../cli/subprocess-launch.js";
const spec = createCliSubprocessLaunchSpec({
entry: "node",
args: ["--max-old-space-size=4096", "dist/worker.js"],
env: { DAEMON_WORKER_ROLE: "worker", OPENAI_API_KEY: process.env.OPENAI_API_KEY, ...process.env },
});
console.log("Run this command to debug the worker:");
console.log(spec.command);
Execute the printed command in your terminal. Any stack trace in stderr points to the failing module, often within packages/ai.
Cleanup Orphaned Processes
Previous crashes may leave detached workers holding file descriptors. The daemon attempts cleanup via killTrackedDetachedChildren() in utils/shell.ts, but manual intervention is sometimes required:
ps -ef | grep prime-agent
kill -9 <pid>
Removing orphaned processes frees the socket and allows the supervisor to claim ownership.
Environment and Logging
Enable Debug Logging
Prime Agent writes daemon-specific logs to the path returned by getDaemonLogPath(). Start with --log-level debug to capture socket-binding attempts, worker-spawn commands, and uncaught exceptions:
./prime-agent.sh --mode daemon \
--daemon-socket /tmp/prime-agent.sock \
--log-level debug
Review ~/.prime-agent/daemon.log for timestamps and error stacks.
Validate Environment Variables
Workers exit early if required credentials are missing. The daemon-client-env.ts module validates variables like OPENAI_API_KEY before spawning.
Ensure all API keys are present in your shell or ~/.prime-agent/env:
export OPENAI_API_KEY="sk-..."
Missing variables appear as clear errors in the debug log.
Run Built-in Benchmarks
The repository includes scripts/bench-daemon-startup.mjs to stress-test the startup sequence and surface race conditions:
node scripts/bench-daemon-startup.mjs
Failures in this script pinpoint the exact line in daemon-mode.ts or daemon-supervisor.ts where the crash occurs.
Consult Regression Tests
The test suites in packages/coding-agent/test/daemon-supervisor-monitor.test.ts and worker-recovery-journal.test.ts demonstrate expected error messages and recovery logic. Reviewing these tests clarifies what constitutes normal versus abnormal behavior in the supervisor's lifecycle management.
Summary
- Remove stale sockets before starting the daemon to prevent bind failures in
daemon-mode.ts. - Check
WorkerRecoveryJournal.readLatest()to identify which initialization stage crashed the worker. - Verify
DaemonSupervisorownership to avoid conflicts from orphaned daemon instances. - Launch workers manually using
createCliSubprocessLaunchSpec()to bypass the daemon and see provider errors directly. - Enable
--log-level debugand inspectgetDaemonLogPath()output for the definitive error trace.
Frequently Asked Questions
Why does the daemon fail with "socket already in use"?
This error occurs when prepareDaemonSocketPath() in daemon-socket.ts detects an existing file at the socket path. A previous daemon instance may have crashed without cleaning up the socket file. Delete the stale socket with rm -f <path> and restart the daemon. If the error persists, check for orphaned processes holding the file descriptor using lsof | grep prime-agent.
How do I determine which environment variable is missing when a worker crashes?
The daemon-client-env.ts module logs specific missing keys during worker initialization. Enable debug logging with --log-level debug and check the daemon log (from getDaemonLogPath() in config.ts) for entries labeled with the missing variable name. Alternatively, run the worker manually using the launch spec from createCliSubprocessLaunchSpec() to see the error printed directly to stderr.
What causes the "Another daemon supervisor already holds ownership" error?
The DaemonSupervisor class uses assertDaemonSupervisorOwnerCurrent() to ensure only one daemon instance manages workers. This error appears when a previous daemon crashed but left child processes running, or when two daemons are configured to use the same socket. Terminate all orphaned prime-agent processes with kill -9, delete the socket file, and restart to establish fresh ownership.
How can I test a worker process without starting the full daemon?
Import createCliSubprocessLaunchSpec() from cli/subprocess-launch.js to generate the exact Node.js command the supervisor uses. Set the DAEMON_WORKER_ROLE environment variable to "worker" and include any required API keys. Running this command in a standalone terminal reveals provider-specific stack traces that the daemon might obscure, allowing you to debug model loading or inference errors directly.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →