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.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗