[macOS] Codex App can create a persistent self-restart loop and may re-run a side-effecting tool call after recovery

Resolved 💬 2 comments Opened Jul 15, 2026 by jeremy9682 Closed Jul 15, 2026

What version of the Codex App are you using?

ChatGPT/Codex App 26.707.72221 (build 5307), bundled codex-cli 0.144.2.

What subscription do you have?

Paid ChatGPT subscription (exact tier omitted from the public report).

What platform is your computer?

macOS 26.5.1 build 25F80, Darwin arm64.

What issue are you seeing?

Codex App entered a severe self-restart loop after an agent tried to restart its own host application once.

The agent correctly recognized that quitting the host would kill its current app-server, but it used the legacy launchctl submit mechanism as if it were a one-shot scheduler. It submitted a finite helper with this shape:

sleep -> quit ChatGPT -> wait for exit -> open ChatGPT -> helper exits

Live launchd state showed the job as type=Submitted, OnDemand=false, last exit code=0, with runs increasing from 59 to 64 within seconds. Each helper exit caused launchd to start it again, so ChatGPT and the bundled Codex app-server were killed and reopened approximately every 11.5 seconds.

The same Codex task later repeated the same design under a second label after an unrelated browser-navigation timeout. Once both jobs overlapped, restarts sometimes occurred roughly every 5-10 seconds.

Observed local impact during a 50-minute window:

  • 123 ChatGPT service exits/removals.
  • 96 observed end/relaunch events for the first Submitted job.
  • 51 observed end/relaunch events for the second Submitted job.
  • At least 72 app-server startups in the first wave and 50 in the second.
  • Active Codex tasks were repeatedly interrupted.

The agent reported DONE_WITH_CONCERNS after launchctl submit returned success, but it never checked launchctl print, OnDemand, runs, TTL, self-removal, or rollback state. scheduled was treated as proof that the one-shot restart was safe.

There is also a possible recovery/idempotency issue. The first label was confirmed removed, then appeared again about 11 minutes later. The affected rollout contains only the original explicit submit tool call for that label. This strongly suggests a resume/recovery re-execution or another hidden re-submission path, but I do not have a direct replay event, so this part is not claimed as proven.

What steps can reproduce the bug?

This involved a destructive host restart, so I do not recommend reproducing it on a normal workstation.

The observed path was:

  1. In Codex App on macOS, a task determines that an environment change requires one restart of the host app.
  2. The user authorizes one restart.
  3. The agent runs launchctl help submit but does not inspect the man page or live job semantics.
  4. The agent describes launchctl submit as a system-level one-shot task and submits a short-lived helper that quits and reopens the host.
  5. The helper exits normally, but the Submitted job starts again and repeats the host restart indefinitely.
  6. After external cleanup, continuing the same task can cause the agent to repeat the same design under another label.

Relevant identifiers for OpenAI internal tracing:

  • Thread/session: 019f6525-1db4-7252-853b-ad2c682dc368
  • First restart turn: 019f6578-78da-7643-b1d7-ecddb4b2eaff
  • Second restart turn: 019f6588-aa16-7951-8428-e5747dba6010

I can provide redacted rollout and desktop log excerpts privately if needed.

What is the expected behavior?

  • Codex should never use launchctl submit as a one-shot host-restart scheduler.
  • Permission to restart once must mean at most one restart, not registration of a persistent job.
  • Host restart orchestration should use a supported, detached, observable one-shot helper with TTL and cleanup.
  • Before reporting success, Codex should verify the live postcondition: job type, run count, cleanup path, and whether the host restarted exactly once.
  • A completed side-effecting tool call must not be executed again after app-server recovery. Interrupted calls with unknown outcome should require a read-only external-state check before any retry.
  • Repeated host exits/launches should trigger a safe recovery mode that suppresses additional system mutations.

Additional information

Confirmed facts:

  • Two explicit launchctl submit calls were made by the same Codex task for finite host-restart helpers.
  • Both produced repeating Submitted jobs.
  • The agent did not run a cleanup or postcondition check.
  • The first removed label later reappeared.

Not yet proven:

  • The exact mechanism that recreated the first removed label. App-server resume/replay is the leading explanation, but OpenAI needs to confirm it from internal recovery logs and call_id handling.

Suggested product fixes:

  1. Add a built-in safe host-restart operation instead of letting the model compose launchd commands.
  2. Block or strongly gate launchctl submit, especially when the payload is finite or quits the host app.
  3. Persist at-most-once tool-call completion by call_id across app-server restarts.
  4. Mark outcome-unknown calls and require state reconciliation before retry.
  5. Add restart-storm detection and automatically disable further mutating tool calls.
  6. Require a postcondition gate for system scheduler mutations.

All usernames, repository/customer details, proxy configuration, and business data have been removed from this report.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗