You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
daemon start reports success when the daemon cannot bind its HTTP port #981
simlock daemon start prints Daemon running and the console URL, and exits 0, when the daemon cannot bind its HTTP port. The daemon logs HTTP frontend failed to start with EADDRINUSE, then stops itself once its startup read finishes.
Seen with the port held by another process (console lane, e2e/console/daemon-start-failure.spec.ts on bug/933-repro):
src/cli/environment.ts:115: the CLI's start ends once status.get answers, and it answers while the daemon is still starting.
src/daemon/main.ts:777 and :840: the bind failure is logged at once, but the daemon stops only after daemon.start() (convergence) ends. A slow startup read leaves a long window.
The user is told the daemon and console are up when both are about to go away. Found while triaging #933.
Confirmed; a defect (cli.mdx: a daemon that exits while starting fails the command with DAEMON_STARTUP_FAILED). bug/981-repro, e2e/start-refusal.test.ts, "simlock daemon start prints DAEMON_STARTUP_FAILED naming EADDRINUSE and the port, exit 1, and never Daemon running". Port held, listManaged slowed to 5 s, so no race.
pnpm test:e2e e2e/start-refusal.test.ts
AssertionError: expected 'Daemon running\nConsole: http://127.0…' not to contain 'Daemon running'
❯ e2e/start-refusal.test.ts:56:33
By hand: the CLI exits 0 in 0.36 s; the daemon logs Daemon failed to start: listen EADDRINUSE 3 s later.
Root cause
src/cli/commands/daemon.ts:94-102 returns on the first status.get answer, and the startup probe accepts starting (src/cli/environment.ts:116). Health turns running at src/daemon/server.ts:598 whether or not HTTP listens, and main.ts:828-840 stops only after convergence. So a CLI that waits for running can still print success when convergence beats the bind failure (#933's one start in seven).
Simplest fix
main.ts: the converge hook (:699 for the worker, :1078 for the gateway) also awaits gatewayStarted. A failed bind then fails the start: health failed, no daemon.started, stop reason stays http-start-failed.
daemon start: after a launch, poll status.get until health is no longer starting. An exit or a lost connection goes through connectAfterExit, which already reports the daemon's own error.
Alternatives rejected
CLI wait only: passes this test but keeps the race when convergence finishes first.
Stop as soon as the bind fails: brings back the B6 event-order bug, and the probe can still answer first.
Risk
daemon start now waits for convergence. status-while-starting.test.ts:17 already says it does. The comments at e2e/helpers/env.ts:296 and http-daemon.ts:10 become wrong.
docs/internal/flows/startup.md:86 and the src/daemon/AGENTS.md bind-failure line need updating.
Through a service there is no child: check a failed start shows as a lost connection, not a 20 s wait.
Maintainer accepted the triage report's proposed fix, both halves. The daemon's startup step waits for the HTTP listener (worker and gateway), so a failed bind fails the start with stop reason http-start-failed. After launch, daemon start waits until health leaves starting, and if the daemon exits or the connection drops it reports the daemon's own error through the existing exit path.
Tests (relayed from the maintainer at delivery start): e2e/start-refusal.test.ts passes with the CLI half alone, so the fix adds a deterministic test that pins the daemon half: when the HTTP listener fails to bind, startup does not report running and the start fails with stop reason http-start-failed, with no timing race (for example a unit test with the bind failing before or after convergence).
Out of scope: #985 (warm pool logs "database is not open" on an early stop) is triaged separately.
daemon start's wait for health to leave starting is bounded, above the daemon's 60 s startup-read cap. On expiry it fails with DAEMON_STARTUP_FAILED saying the daemon is still starting, and leaves the daemon running (decided in delivery, pending the maintainer).
Tests: a CLI unit test with a fake clock. A daemon that stays starting past the bound fails the start with DAEMON_STARTUP_FAILED.
Merged: #990 (#981)
Parked: none
Waiting: none
Decided: #981 — how long does daemon start wait for starting to end? → a bounded 90 s, then DAEMON_STARTUP_FAILED with the daemon left running. #981 — what does the user see when a daemon is already stopping during start? → DAEMON_STARTUP_FAILED with the daemon's own error, replacing #710's DAEMON_STOPPING pass-through. Both are pending the maintainer.
Activity
V3RON commentedon Oct 25, 2026
Reproduction
Confirmed; a defect (
cli.mdx: a daemon that exits while starting fails the command withDAEMON_STARTUP_FAILED).bug/981-repro,e2e/start-refusal.test.ts, "simlock daemon start prints DAEMON_STARTUP_FAILED naming EADDRINUSE and the port, exit 1, and never Daemon running". Port held,listManagedslowed to 5 s, so no race.By hand: the CLI exits 0 in 0.36 s; the daemon logs
Daemon failed to start: listen EADDRINUSE3 s later.Root cause
src/cli/commands/daemon.ts:94-102returns on the firststatus.getanswer, and the startup probe acceptsstarting(src/cli/environment.ts:116). Health turnsrunningatsrc/daemon/server.ts:598whether or not HTTP listens, andmain.ts:828-840stops only after convergence. So a CLI that waits forrunningcan still print success when convergence beats the bind failure (#933's one start in seven).Simplest fix
main.ts: the converge hook (:699for the worker,:1078for the gateway) also awaitsgatewayStarted. A failed bind then fails the start: healthfailed, nodaemon.started, stop reason stayshttp-start-failed.daemon start: after a launch, pollstatus.getuntil health is no longerstarting. An exit or a lost connection goes throughconnectAfterExit, which already reports the daemon's own error.Alternatives rejected
Risk
daemon startnow waits for convergence.status-while-starting.test.ts:17already says it does. The comments ate2e/helpers/env.ts:296andhttp-daemon.ts:10become wrong.docs/internal/flows/startup.md:86and thesrc/daemon/AGENTS.mdbind-failure line need updating.Side findings
Written by an agent.
V3RON commentedon Oct 25, 2026
Decision
Maintainer accepted the triage report's proposed fix, both halves. The daemon's startup step waits for the HTTP listener (worker and gateway), so a failed bind fails the start with stop reason
http-start-failed. After launch,daemon startwaits until health leavesstarting, and if the daemon exits or the connection drops it reports the daemon's own error through the existing exit path.Written by an agent.
V3RON commentedon Oct 25, 2026
Decision
Tests (relayed from the maintainer at delivery start):
e2e/start-refusal.test.tspasses with the CLI half alone, so the fix adds a deterministic test that pins the daemon half: when the HTTP listener fails to bind, startup does not reportrunningand the start fails with stop reasonhttp-start-failed, with no timing race (for example a unit test with the bind failing before or after convergence).Out of scope: #985 (warm pool logs "database is not open" on an early stop) is triaged separately.
Written by an agent.
V3RON commentedon Oct 25, 2026
Decision
daemon start's wait for health to leavestartingis bounded, above the daemon's 60 s startup-read cap. On expiry it fails withDAEMON_STARTUP_FAILEDsaying the daemon is still starting, and leaves the daemon running (decided in delivery, pending the maintainer).Tests: a CLI unit test with a fake clock. A daemon that stays
startingpast the bound fails the start withDAEMON_STARTUP_FAILED.Details: PR #990, "Decision taken".
Written by an agent.
test: daemon start's wait for health to leave starting is bounded abo…
feat(cli): daemon start gives up on a daemon still starting after 90 s (
refactor(cli): split the staged daemon's status answer in the CLI tes…
test(cli): daemon start's bounded wait is pinned at 90 s, not just pa…
test(daemon): the late-bind start test holds the bind on a gate and c…
fix(cli): a stopping daemon at the startup probe is reported as its o…
refactor(cli): keep connect under the complexity bound (#981)
test(console): the daemon start failure spec expects the non-zero exi…
V3RON commentedon Oct 25, 2026
Delivery run
Merged: #990 (#981)
Parked: none
Waiting: none
Decided: #981 — how long does
daemon startwait forstartingto end? → a bounded 90 s, thenDAEMON_STARTUP_FAILEDwith the daemon left running. #981 — what does the user see when a daemon is already stopping during start? →DAEMON_STARTUP_FAILEDwith the daemon's own error, replacing #710'sDAEMON_STOPPINGpass-through. Both are pending the maintainer.Written by an agent.
chore(agents): process review 2026-10-25: turbo, bug:new triage, feat…