In-process app-server drops AgentMessageDelta under backpressure, truncating TUI output
What version of Codex CLI is running?
codex-cli 0.144.5
Which model were you using?
gpt-5.6-sol through a custom Responses HTTP provider (supports_websockets = false). The provider does not appear relevant: the complete response was persisted before the TUI transport loss.
What platform is your computer?
Linux xyz 6.18.12-1-MANJARO x86_64 GNU/Linux
Terminal multiplexer: tmux 3.6a
What issue are you seeing?
The interactive CLI TUI can render a truncated assistant answer when its in-process app-server notification queue is full.
In one occurrence, the API response and rollout contained the complete ending:
````markdown
设置后,后续请求会带:
"reasoning": {
"effort": "low",
"summary": "detailed",
"context": "all_turns"
}
````
but the live terminal ended at:
设置后,后续请求 "
The persisted rollout contained the complete assistant response_item and task_complete.last_agent_message, so this is not model/API truncation. The complete HTTP Responses stream was also independently captured. The TUI consolidation log recorded a shorter source (source_len=1902, versus 2008 bytes in the complete persisted answer).
At the same timestamp, logs_2.sqlite contained repeated warnings:
dropping in-process server notification (queue full)
dropping in-process app-server event because consumer queue is full
app-server event consumer lagged
This initially looked like fenced-JSON Markdown corruption, but the JSON block merely occurred after the missing transcript delta.
Deterministic source-level reproduction
Current main at 56395bddaf26eb2829387ca6a417bf9128e5b239 still has inconsistent must-deliver classification between the two in-process queue layers.
Add this test to codex-rs/app-server/src/in_process.rs:
use codex_app_server_protocol::AgentMessageDeltaNotification;
#[test]
fn guaranteed_delivery_helpers_cover_agent_message_deltas() {
assert!(server_notification_requires_delivery(
&ServerNotification::AgentMessageDelta(AgentMessageDeltaNotification {
thread_id: "thread-1".to_string(),
turn_id: "turn-1".to_string(),
item_id: "item-1".to_string(),
delta: "```json\n{\"reasoning\": true}\n```".to_string(),
})
));
}
Then run:
cd codex-rs
cargo test -p codex-app-server guaranteed_delivery_helpers_cover_agent_message_deltas --lib -- --exact
The assertion fails deterministically because codex-rs/app-server/src/in_process.rs::server_notification_requires_delivery currently matches only:
TurnCompleted
ThreadSettingsUpdated
ExternalAgentConfigImportCompleted
As a result, AgentMessageDelta reaches the event_tx.try_send(...) branch and is dropped when that queue is full.
By contrast, codex-rs/app-server-client/src/lib.rs::server_notification_requires_delivery correctly treats the following as lossless and forwards them with send(...).await:
ItemCompleted
AgentMessageDelta
PlanDelta
ReasoningSummaryTextDelta
ReasoningTextDelta
The second layer therefore cannot preserve a transcript delta that the first layer has already dropped.
Expected behavior
Assistant, plan, and reasoning transcript deltas and authoritative item/turn completion notifications should never be dropped under in-process backpressure. The TUI should render the same assistant message that is persisted in the rollout.
Both in-process queue layers should share the same must-deliver classification, or the app-server layer should include all transcript and completion notification variants and use send(...).await for them.
Additional information
Related reports:
- #20777 reported the same
dropping in-process server notification (queue full)warning and was closed as a duplicate of #20601. - When closing #20601, maintainers asked users to file a new report if similar symptoms recurred.
- PR #21870 addressed TUI active-thread routing, but its changed files were limited to
codex-rs/tui; it did not align the lower-level helper incodex-rs/app-server/src/in_process.rs. - A comment on #20601 explicitly proposed aligning the lower-level helper for
ItemCompletedand assistant/plan/reasoning deltas, which matches the remaining gap in currentmain.
No raw rollout or SQLite database is attached because they contain private prompts and local paths. A sanitized thread/session identifier can be provided privately or through /feedback if useful.
6 Comments
Hey, I had a look at this against current main (
2deed3fb9c) on macOS.The lower in-process queue only treated turn completion, thread settings, and external-import completion as lossless. That meant assistant, plan, and reasoning deltas could be dropped before the higher-level client had a chance to preserve them.
I've prepared a focused fix that aligns the lower queue with the client's transcript classification.
ItemCompleted,AgentMessageDelta,PlanDelta,ReasoningSummaryTextDelta, andReasoningTextDeltanow usesend(...).awaitunder pressure. Command-output deltas stay best-effort, so high-volume progress events can still be dropped instead of blocking the whole transport.The patch is 98 changed lines in one file: 84 additions and 14 deletions. Most of that is expanded regression coverage; the production behavior change is the small must-deliver classification above.
I added regression coverage for the must-deliver variants and for the command-output exception. The focused tests pass, and
just fix -p codex-app-serverplusjust fmtare clean.I also ran
just test -p codex-app-server: 954 tests passed. Eight unrelated integration tests failed locally—four because the scoped build didn't provide thecodex/test_stdio_serverhelper binaries, and four macOS zsh-fork tests timed out.Prepared commit: https://github.com/TheJhyeFactor/codex/commit/3aeb0aae8ebf5c9ca28a024ee983b8afadb5d5d7
I haven't opened a PR because the contribution guide asks external contributors to wait for an invitation. If this direction looks right, the branch is ready for an invited submission.
Independent Windows reproduction: safety modal becomes unresponsive while the backend continues
This incident appears to connect the in-process notification backpressure described here with the cybersecurity-interruption/modal reports in #32225 and #32139.
Environment
0.144.6gpt-5.6-solcmd.exeAffected identifiers:
019f8071-5965-7a81-b17b-0940e48ed03c019f81a7-1408-7be1-a8de-449ba0fc19082380What happened
During a long-running specification amendment, the CLI displayed:
The entire TUI then stopped accepting input. Local
Esc, terminal resize, and maximize/restore had no effect, so the user could not choose Keep waiting or dismiss the modal.The terminal itself and the Codex process remained alive. Independent monitoring showed that the Codex session continued appending distinct events and performing meaningful work behind the stale modal. During incident inspection,
logs_2.sqliteshowed more than 300 occurrences of:clustered within approximately six seconds for the affected process.
This was not an immediate context-window failure. The thread reached approximately 213k of a 258.4k context window, successfully auto-compacted to approximately 60k, and continued making substantive edits afterward.
Eventually the backend also stopped making progress. The final recorded event was at
2026-07-21T00:29:41.973Z, at a pending Git commit tool call. No corresponding tool result ortask_completereached the turn. The visible TUI remained stuck at the old safety modal.Recovery
2380.git diff --cached --check.codex resume 019f8071-5965-7a81-b17b-0940e48ed03c.No repository work was lost, but the modal offered no usable recovery path and the final commit had to be completed outside the interrupted turn.
Why this seems related to this issue
The safety check was the transition immediately preceding the wedge, but the evidence does not establish whether it is the root cause or simply a reliable high-event-pressure trigger. The defensive request may also have been a classifier false positive; the UI deadlock is independently a client reliability bug even if the safety review itself was intentional.
Expected behavior
Reproduction characteristics
The occurrence was intermittent, but the observed workload had these characteristics:
I am not attaching the raw rollout or SQLite database publicly because they contain private repository content and local paths. The identifiers above are provided so OpenAI can correlate the incident internally. A scoped private upload can be supplied if requested.
Thanks for the bug report and analysis. This will be addressed in the next release.
Thanks, Eric. I’d already prepared and tested a focused patch for this above. It updates the lower in-process queue to preserve
ItemCompletedand assistant, plan, and reasoning transcript events under backpressure, while keeping high-volume command output best-effort. The app-server suite completed with 954 passing tests, alongside the unrelated local failures I documented.Did the team get a chance to review that work, and does the next-release fix follow the same approach? If my branch is still useful, I’m happy to submit it through the invitation process.
@TheJhyeFactor, I didn't look at your branch. Codex wrote the fix that I merged. We generally don't accept outside code contributions because it's way more efficient for Codex to write fixes than for us to review external changes. We do appreciate repro steps and root cause analyses though. Those are helpful!
Thanks for the clarification, @etraut-openai .
I understand why the team prefers using Codex to implement fixes internally.
I’d still really like to become more involved with the project beyond occasionally reporting bugs. I’m happy to focus on the areas that are most useful to the team reliable reproductions, root-cause analysis, regression tests, validating fixes across platforms, or identifying gaps between related components.
I use Codex heavily and enjoy digging into these issues personally,
so I’d appreciate any guidance on the best way to contribute consistently and potentially work toward being invited to submit changes in the future.