Codex CLI on macOS used `launchctl submit` for child dispatches, causing persistent respawn loops and runaway token usage
Summary
A Codex CLI orchestration run on macOS dispatched child codex exec workers via launchctl submit instead of a one-shot detached process. That registered launchd-managed jobs which kept re-running the same codex exec "$(cat CODEX_BRIEF.md)" commands from the same worktrees for roughly 14 days, causing large unintended token usage until I manually removed the jobs.
This appears to be an unsafe implementation choice by Codex, not an explicit user request for persistent background jobs.
Environment
- Codex CLI observed in logs:
0.128.0through0.130.0 - macOS:
26.4(Apple Silicon) - Machine timezone:
America/Chicago - Usage mode: local
codex exec/ headless orchestration on macOS
What happened
A parent Codex session received a prompt telling it to:
- create worktrees
- write
CODEX_BRIEF.mdfiles - launch 4 headless Codex workers
- exit
Instead of using a one-shot detached launch, the parent session used launchctl submit -l <label> -- /bin/bash -lc '... exec codex exec ... "$(cat CODEX_BRIEF.md)" ...' for each worker.
Those labels stayed registered under launchd and the workers kept restarting/re-running the same briefs long after the original task should have finished.
Why this looks like a Codex bug
The prompt asked Codex to dispatch workers, but it did not ask Codex to install persistent OS-managed background jobs with restart behavior.
On macOS, launchctl submit is not a harmless substitute for nohup ... & here. It creates a launchd-managed job. In practice this meant the child jobs persisted and kept coming back, repeatedly consuming tokens without explicit user intent.
Concrete evidence from local session logs
Parent bootstrap session:
- local session file timestamp: May 6, 2026 10:12:49 PM CDT
- session metadata shows
originator: "codex_exec",source: "exec" - prompt told Codex to create worktrees, write
CODEX_BRIEF.md, fire 4 headlesscodex execdispatches, then exit - a few minutes later the same session ran 4
launchctl submitcommands for labels like: codex-wave-3-boundary-adapter-fixcodex-phase-12-3h-5-spec-amendmentscodex-phase-12-3f-9-quality-gate-smokecodex-phase-12-3g-7-quality-gate-smoke
One concrete same-lane rerun example on May 20, 2026:
- a
12.3f.9lane run started at 6:21:05 PM CDT and completed at 6:22:15 PM CDT - the same lane started again at 6:22:16 PM CDT in the same worktree with the same brief
Both runs had originator: "codex_exec", source: "exec", and the same lane prompt injected as the user message.
Scale of impact observed locally
Counting true session starts from the first session_meta line only, I found approximately:
wave-3-boundary-adapter: 43,759 startsphase-12-3h-5: 50,845 startsphase-12-3f-9: 20,644 startsphase-12-3g-7: 52,385 starts
Total: 167,633 lane-session starts across the runaway window.
This was the main driver of unexpected token burn on the machine.
Expected behavior
If Codex is asked to dispatch child workers, it should:
- use a one-shot detached process model unless the user explicitly asks for persistent background jobs
- never silently install OS-level persistent launchers / service managers without explicit user consent
- avoid
launchctl submit/ persistent launchd registration for ordinary worker dispatch - clean up any temporary background job registration automatically if it must use OS facilities
- ideally detect self-reinvocation / repeated-brief loops and stop or warn
Suggested safeguards
- hard-block or strongly discourage
launchctl submit/launchctl bootstrap/ similar service-manager registration unless the user explicitly requests persistence - add a policy guard around launching
codex execfrom inside Codex when the launch mechanism is persistent or restart-capable - add loop detection when the same worktree + same prompt/brief are being started repeatedly in a short window
- surface a warning before creating any host-level job that can survive the current interactive session
Notes
I already manually removed the launchctl jobs and killed the live workers, so the immediate runaway is resolved locally. The issue here is the underlying Codex behavior on macOS.