Codex CLI on macOS used `launchctl submit` for child dispatches, causing persistent respawn loops and runaway token usage

Open 💬 0 comments Opened May 21, 2026 by ty13r

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.0 through 0.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.md files
  • 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 headless codex exec dispatches, then exit
  • a few minutes later the same session ran 4 launchctl submit commands for labels like:
  • codex-wave-3-boundary-adapter-fix
  • codex-phase-12-3h-5-spec-amendments
  • codex-phase-12-3f-9-quality-gate-smoke
  • codex-phase-12-3g-7-quality-gate-smoke

One concrete same-lane rerun example on May 20, 2026:

  • a 12.3f.9 lane 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 starts
  • phase-12-3h-5: 50,845 starts
  • phase-12-3f-9: 20,644 starts
  • phase-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 exec from 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.

View original on GitHub ↗