Summary
The exit-reaper added in #1695 (registerExitCleanup in src/cli/operations/dev/dev-server.ts) fixes the orphaned-child-holding-the-port bug (#1690) for CodeZip dev servers, but not for Container agents. On the process.exit(0) path — i.e. Ctrl+C / kill $PID, which is the exact scenario #1695 targets — a Container dev server keeps running and continues to hold the mapped host port.
Root cause
The reaper reaps the child's process group:
// dev-server.ts
process.kill(-pid, 'SIGKILL');
For ContainerDevServer, getSpawnConfig() returns docker/podman/finch run --rm --name <name> -p <hostPort>:<internalPort> ..., so this.child is the run client, not the server. The actual server runs inside the container, owned by dockerd/containerd in a separate process tree. Group-killing the run-client's process group does not stop the container:
--rm cleans up on container stop/exit, not on client death — so it never triggers here.
- SIGKILL to the run client does not propagate to the container for the client/daemon split (true for Docker, Podman/conmon, and Finch/Lima).
Container teardown lives only in the ContainerDevServer.kill() override (spawn(runtimeBinary, ['stop', containerName])), and the process.exit(0) path bypasses kill() entirely — that bypass is the whole reason the reaper exists.
Repro
# On a Container agent (agent with a Dockerfile / --type container)
agentcore dev --logs --port 9137 &
DEV_PID=$!
# wait for the port to bind, then:
kill $DEV_PID # or Ctrl+C in an interactive session
sleep 4
lsof -ti :9137
# actual: container still running, port held (reparented; only freed by manual `docker stop`)
# expected: empty — port freed
The next agentcore dev then fails on the held port until the user manually runs docker stop <name>. (Note: the base prepare() rm -f clears a stale name on next launch, but not a still-running container's live port binding.)
Suggested fix
Make the exit reaper container-aware — on the exit path, stop the container by name (mirroring the kill() override's runtimeBinary stop <containerName>) rather than group-killing the run-client pid. Since process.exit's 'exit' handler must be synchronous, a spawnSync(runtimeBinary, ['stop', containerName]) (or rm -f) is likely the right primitive there.
Context
Related
Summary
The exit-reaper added in #1695 (
registerExitCleanupinsrc/cli/operations/dev/dev-server.ts) fixes the orphaned-child-holding-the-port bug (#1690) for CodeZip dev servers, but not for Container agents. On theprocess.exit(0)path — i.e.Ctrl+C/kill $PID, which is the exact scenario #1695 targets — a Container dev server keeps running and continues to hold the mapped host port.Root cause
The reaper reaps the child's process group:
For
ContainerDevServer,getSpawnConfig()returnsdocker/podman/finch run --rm --name <name> -p <hostPort>:<internalPort> ..., sothis.childis the run client, not the server. The actual server runs inside the container, owned bydockerd/containerdin a separate process tree. Group-killing the run-client's process group does not stop the container:--rmcleans up on container stop/exit, not on client death — so it never triggers here.Container teardown lives only in the
ContainerDevServer.kill()override (spawn(runtimeBinary, ['stop', containerName])), and theprocess.exit(0)path bypasseskill()entirely — that bypass is the whole reason the reaper exists.Repro
The next
agentcore devthen fails on the held port until the user manually runsdocker stop <name>. (Note: the baseprepare()rm -fclears a stale name on next launch, but not a still-running container's live port binding.)Suggested fix
Make the exit reaper container-aware — on the exit path, stop the container by name (mirroring the
kill()override'sruntimeBinary stop <containerName>) rather than group-killing the run-client pid. Sinceprocess.exit's'exit'handler must be synchronous, aspawnSync(runtimeBinary, ['stop', containerName])(orrm -f) is likely the right primitive there.Context
process.exitpath before that PR. fix: reap orphaned dev-server child on process exit #1695 is a strict improvement (fixes CodeZip); this issue tracks extending the same protection to containers.Related
agentcore devleaves orphaned Uvicorn process on Ctrl+C #1690 /agentcore devcan leave orphaned processes occupying ports #1440 (original orphaned-port bug)