Factory reset restarts the app but leaves the old backend holding :4242/:4243, so the next setup cannot boot
Found by driving reset in CI for the first time (CIRISAgent#1150, five-platform gate run 34072801004, macOS and Linux desktop legs).
What happens
- Complete setup, reach Interact.
- Login footer → Reset device → confirm.
factoryReset() runs: clears the signing key, deletes the .env, sets _resetSuccess, and the app restarts into the wizard. All of that works — logout and reset_device both pass, in ~1.6s and ~5.1s.
- Run setup again. The backend the app spawns exits 1 before serving:
Setting CIRIS_FORCE_FIRST_RUN=1
Server PID: 22351
Waiting for server to be ready...
[FAIL] backend failed: Server process exited unexpectedly (exit code: 1)
and the backend's own log says why:
ERROR - Edge runtime initialization failed (REQUIRED foundation dep):
Edge transport ports are held by another process
(ReticulumTransport::new: ...)
CRITICAL - Runtime initialization failed: Initialization sequence failed
ERROR - [EXIT] Fatal error in main (PID=22351)
The previous backend is still holding :4242 (Edge transport) and :4243 (node read API) when the new one starts.
Why we think it is the restart path, not the backend
The agent releases those ports on a graceful shutdown — that is CIRISAgent#1102, fixed in 2.10.0: CIRISRuntime.shutdown() now calls shutdown_node() then Edge.close() before exit. That release is bypassed when the process is killed rather than asked to stop, because the transport is Rust threads that outlive a kill -9.
So the question is what the app does to its backend across a factory reset. If it kills the process (or drops it and starts a second), the old transport keeps the ports and the next boot cannot bind. If it asks the backend to shut down and waits, the 2.10.0 release path runs and the ports come back.
We are not certain which of those the app does — that is your side, and we would rather ask than guess.
Why it matters beyond CI
This is not a harness-only sequence. Reset device → set up again is a normal user action, and on desktop it is the documented escape hatch from a broken install (the always-on link in the Login footer, #794 Bug C). If the ports are still held, the user's next setup fails with a message about Edge transport that tells them nothing they can act on.
We worked around it in the gate by polling until 4242/4243/8080 are free before the next setup, capped at 60s, failing loudly if they never are. That keeps CI honest; it does not help a user.
What would settle it
Either the app asks the backend to stop and waits for it (letting the 2.10.0 graceful release run), or it kills it and then waits for the ports before starting the next one. If you already do the former and the ports are still held, that is ours and we will reopen CIRISAgent#1102 with your evidence.
Agent-side references: CIRISAgent#1102 (transport outliving shutdown), CIRISAgent#1150 (the gate that found this).
🤖 Generated with Claude Code
Factory reset restarts the app but leaves the old backend holding :4242/:4243, so the next setup cannot boot
Found by driving reset in CI for the first time (CIRISAgent#1150, five-platform gate run 34072801004, macOS and Linux desktop legs).
What happens
factoryReset()runs: clears the signing key, deletes the.env, sets_resetSuccess, and the app restarts into the wizard. All of that works —logoutandreset_deviceboth pass, in ~1.6s and ~5.1s.and the backend's own log says why:
The previous backend is still holding
:4242(Edge transport) and:4243(node read API) when the new one starts.Why we think it is the restart path, not the backend
The agent releases those ports on a graceful shutdown — that is CIRISAgent#1102, fixed in 2.10.0:
CIRISRuntime.shutdown()now callsshutdown_node()thenEdge.close()before exit. That release is bypassed when the process is killed rather than asked to stop, because the transport is Rust threads that outlive akill -9.So the question is what the app does to its backend across a factory reset. If it kills the process (or drops it and starts a second), the old transport keeps the ports and the next boot cannot bind. If it asks the backend to shut down and waits, the 2.10.0 release path runs and the ports come back.
We are not certain which of those the app does — that is your side, and we would rather ask than guess.
Why it matters beyond CI
This is not a harness-only sequence. Reset device → set up again is a normal user action, and on desktop it is the documented escape hatch from a broken install (the always-on link in the Login footer, #794 Bug C). If the ports are still held, the user's next setup fails with a message about Edge transport that tells them nothing they can act on.
We worked around it in the gate by polling until
4242/4243/8080are free before the next setup, capped at 60s, failing loudly if they never are. That keeps CI honest; it does not help a user.What would settle it
Either the app asks the backend to stop and waits for it (letting the 2.10.0 graceful release run), or it kills it and then waits for the ports before starting the next one. If you already do the former and the ports are still held, that is ours and we will reopen CIRISAgent#1102 with your evidence.
Agent-side references: CIRISAgent#1102 (transport outliving shutdown), CIRISAgent#1150 (the gate that found this).
🤖 Generated with Claude Code