Release builds block `codex exec` exit ~900 ms flushing Statsig OTLP metrics after the turn completes

Open 💬 2 comments Opened Aug 24, 2026 by praveenvijayan

What version of Codex CLI is running?

codex-cli 0.146.0

What subscription do you have?

Plus

Which model were you using?

_No response_

What platform is your computer?

macOS 26.5 (Apple Silicon); expected on all platforms since the exporter is not OS-gated

What terminal emulator and version are you using (if applicable)?

iTerm2

Codex doctor report

What issue are you seeing?

A release-profile codex enables the Statsig OTLP metrics exporter
(codex-rs/otel/src/config.rs defaults to OtelExporter::Statsig; it is disabled
under cfg!(debug_assertions), which is why debug builds never show this).
During shutdown the exporter flushes over HTTPS to ab.chatgpt.com, holding
process exit long after the turn is finished.

Measured on macOS (Apple Silicon), release build, mock model provider so model
latency is excluded, 30 runs, p50:

| configuration | turn.completed | process exit | shutdown tail |
| -------------------------------- | -------------- | ------------ | ------------- |
| default | 136.1 ms | 1,046.2 ms | 910.1 ms |
| otel.metrics_exporter = "none" | 111.5 ms | 115.9 ms | 4.4 ms |

Interactive sessions pay this once. codex exec one-shots and SDK-style
automation that spawns a process per turn pay it on every turn: ten short
turns take ~9.9 s wall clock with the default config and ~0.7 s with the
exporter off.

What steps can reproduce the bug?

  1. Build codex with --release (or use a released binary).
  2. Run codex exec "say hi" against a mock/fast provider and record the

timestamp of the turn.completed event vs process exit.

  1. Add [otel] metrics_exporter = "none" to config.toml and repeat.
  2. The gap between answer-complete and exit drops from ~910 ms to ~5 ms.

What is the expected behavior?

Process exit should not be held for a blocking network flush after the answer
is complete. Options: flush asynchronously, bound the shutdown flush with a
short deadline (e.g. 100 ms), or start the flush at turn.completed instead of
at exit.

Additional information

_No response_

View original on GitHub ↗

2 Comments

praneethhere · 3 days ago

I reproduced this on codex-cli 0.149.1 on macOS using the released binary and the supported codex exec --ephemeral --json path.

Initial paired measurement:

default: shutdown tail 1172.7 ms
otel.metrics_exporter="none": shutdown tail 311.9 ms
difference: 860.8 ms

Here, “shutdown tail” is measured from receipt of turn.completed to process exit, so model latency is excluded. This confirms the behavior is still present after 0.146.0.

I also traced current main: codex_exec::run_main creates the OtelProvider but returns after run_exec_session without using shutdown_with_timeout, while the interactive TUI already uses the bounded shutdown path.

I’d like to prepare a narrow PR that applies the existing bounded shutdown mechanism to codex exec and adds regression coverage.

praneethhere · 3 days ago

I’ve now reviewed the repository’s contribution policy and understand that Codex does not accept external pull requests, so I won’t open one. The reproduction and code-path notes above are intended as issue-triage and root-cause evidence.

I’m happy to run additional measurements or validate a maintainer-authored fix on macOS with codex-cli 0.149.1.