TUI never renders reasoning summaries (live or on replay) even when data is proven to reach codex_core intact, reproduces with gpt-5.5, no custom provider needed

Open 💬 0 comments Opened Jul 6, 2026 by davidkny22

Summary

The interactive TUI (codex with no subcommand) never renders a reasoning/thinking block for any model, live or on replay, even when the underlying reasoning data is proven to be generated, transmitted, and received correctly. codex exec --json (headless mode) reliably displays the identical reasoning data every time. This reproduces both with a custom model_provider (wire_api = "responses") and with the first-party gpt-5.5 model, so it does not appear to be provider-specific.

I want to be upfront: I was not able to pin down the exact line/commit responsible. I dug as far as I could with RUST_LOG tracing across codex_core, codex_tui, and codex_api, and I'm reporting the full evidence trail below so the Codex team can pick up exactly where I stopped, rather than re-deriving it from scratch.

Environment

  • codex-cli 0.142.5 (npm @openai/codex)
  • Windows 11, PowerShell 7 launched from Windows Terminal
  • Auth: ChatGPT account auth (tokens present in auth.json alongside an API key)
  • Reproduces with both a custom model_provider (local bridge, wire_api = "responses") and the first-party gpt-5.5 model

Config used

# ~/.codex/config.toml (relevant lines)
model = "gpt-5.5"
model_reasoning_effort = "xhigh"
model_reasoning_summary = "detailed"
hide_agent_reasoning = false
show_raw_agent_reasoning = true
# ~/.codex/oc-go.config.toml (custom-provider profile, loaded as an overlay per the documented
# "config.toml, then overlay profile-name.config.toml" behavior)
model = "glm-5.2"
model_provider = "oc_go"
model_catalog_json = 'C:\Users\david\.codex\model-catalogs\oc-go-glm52.json'
model_reasoning_effort = "high"
model_supports_reasoning_summaries = true
model_reasoning_summary = "detailed"
hide_agent_reasoning = false
show_raw_agent_reasoning = true

[model_providers.oc_go]
name = "OpenCode Go GLM-5.2"
base_url = "http://127.0.0.1:4010/v1"
env_key = "LITELLM_MASTER_KEY"
wire_api = "responses"
stream_idle_timeout_ms = 600000

The custom provider is a small local FastAPI bridge that translates an upstream Chat-Completions-style API (which returns a message.reasoning_content field) into the Responses API shape, including proper response.reasoning_summary_part.added / response.reasoning_summary_text.delta / .done SSE events, verified to match Codex's own test fixtures byte-for-byte (see codex-rs/core/tests/common/responses.rs::ev_reasoning_item_added / ev_reasoning_summary_text_delta).

Scenarios reproduced (all three show zero reasoning display in the TUI)

  1. Custom provider, live turn. codex --profile oc-go, ask a multi-step reasoning question. No reasoning block shown, ever, regardless of reasoning_effort (tested medium, high).
  2. Custom provider, replay/resume. codex resume <session-id> on a session where the prior turn's reasoning is confirmed present and well-formed in the persisted rollout JSONL (see below). Still nothing shown for that turn on reload.
  3. First-party gpt-5.5, live turn, default profile, no custom provider at all. Same result: zero reasoning shown, live, with model_reasoning_summary = "detailed" and show_raw_agent_reasoning = true both set.

By contrast, codex exec --json --profile oc-go "<same question>" and codex exec --json "<same question>" (default gpt-5.5) both reliably print a {"type":"item.completed","item":{"type":"reasoning","text":"..."}} event with the real reasoning text, every time, in both cases.

Evidence: the data is intact all the way into codex_core

Persisted rollout JSONL for the custom-provider session (~/.codex/sessions/.../rollout-...jsonl):

{"type":"event_msg","payload":{"type":"agent_reasoning","text":"Let me solve this. First pen: chickens (c) and rabbits (r)...Total legs = 94 + 116 = 210."}}
{"type":"response_item","payload":{"type":"reasoning","id":"rs_35f82be190de41398416f5a47a8960ca","summary":[{"type":"summary_text","text":"Let me solve this...Total legs = 94 + 116 = 210."}],"content":null,"encrypted_content":null,"internal_chat_message_metadata_passthrough":{"turn_id":"..."}}}

RUST_LOG=codex_core=debug,codex_tui=debug,codex_api=trace -c log_dir=... output for a fresh live turn, in order:

TRACE codex_api::sse::responses: SSE event: {"type": "response.reasoning_summary_part.added", "item_id": "rs_b7a7...", "output_index": 0, "summary_index": 0, "part": {"type": "summary_text", "text": ""}}
TRACE codex_api::sse::responses: SSE event: {"type": "response.reasoning_summary_text.delta", "item_id": "rs_b7a7...", "output_index": 0, "summary_index": 0, "delta": "Let me solve this step b"}
... (14 more correctly-shaped delta events, each with integer summary_index and non-empty delta) ...
TRACE codex_api::sse::responses: SSE event: {"type": "response.reasoning_summary_text.done", "item_id": "rs_b7a7...", ..., "text": "Let me solve this step by step...Combined legs: 94 + 116 = 210"}
TRACE codex_api::sse::responses: unhandled responses event: response.reasoning_summary_text.done
TRACE codex_api::sse::responses: SSE event: {"type": "response.reasoning_summary_part.done", ...}
TRACE codex_api::sse::responses: unhandled responses event: response.reasoning_summary_part.done

DEBUG codex_core::stream_events_utils: Output item item=Reasoning { id: Some("rs_b7a7..."), summary: [SummaryText { text: "" }], content: None, encrypted_content: None, ... }
DEBUG codex_core::stream_events_utils: Output item item=Reasoning { id: Some("rs_b7a7..."), summary: [SummaryText { text: "Let me solve this step by step...Combined legs: 94 + 116 = 210" }], content: None, encrypted_content: None, ... }

Notes on the above:

  • The .delta and .part.added events are never logged as "unhandled" (unlike .text.done / .part.done, which correctly are: those variants have no match arm and that's expected/harmless). This means codex-api's SSE parser successfully matched them and constructed ResponseEvent::ReasoningSummaryDelta / ResponseEvent::ReasoningSummaryPartAdded values.
  • codex_core::stream_events_utils (handle_non_tool_response_item, called for both OutputItemAdded and OutputItemDone) logs the item twice, once with the empty placeholder text (from output_item.added) and once with the full final text (from output_item.done), proving the item reaches codex_core with the correct id and complete content in both cases.

Where the trail goes cold

I could not find any log output, at trace level, across codex_core, codex_tui, and codex_api simultaneously, for any of the following, despite the above proving the data was present and well-formed at the point closest to the TUI:

  • ResponseEvent::ReasoningSummaryDelta being matched/processed inside codex_core::session::turn
  • EventMsg::ReasoningContentDelta being constructed or emitted
  • The error_or_panic("ReasoningSummaryDelta without active item") message (which, per the source I could read, is the one documented failure path for this exact situation, and would contain a greppable "active item" substring)
  • ServerNotification::ReasoningSummaryTextDelta reaching codex_tui::chatwidget
  • on_agent_reasoning_delta / on_agent_reasoning_final (the TUI functions that push a history cell), though as far as I could tell these have no logging at all on either their success or no-op paths, so their absence from the log is not informative on its own
  • EventMsg::ItemStarted / EventMsg::ItemCompleted anywhere in the log at all, including for the plain message item in the same turn, which did render correctly in the TUI. Since this string is absent even for content that's known to have rendered fine, I don't think its absence for the reasoning item is meaningful either. It suggests this whole tier of event just isn't logged, full stop.

So: the last hop I can positively confirm working is codex_core receiving and logging the completed Reasoning item with correct text. The last hop I can positively confirm not working is the TUI actually painting something on screen. Everything strictly in between has effectively zero logging coverage in this build, so I can't tell you whether the silent failure is in event-type routing, active_item tracking, the EventMsg to ServerNotification conversion, or the TUI's own render call, only that it's somewhere in that span, and that it fails identically for a fully custom provider and for first-party gpt-5.5.

Suggested next steps for the Codex team

  • Add (even temporary) trace!/debug! instrumentation to whichever function converts ResponseEvent::ReasoningSummaryDelta into EventMsg::ReasoningContentDelta (and its no-active-item failure path), and to on_agent_reasoning_delta/on_agent_reasoning_final in codex-rs/tui/src/chatwidget/streaming.rs. Right now a silent success and a silent failure are indistinguishable from the outside, which is what made this bug so hard to localize.
  • Given the identical symptom occurs on gpt-5.5 (first-party, no custom provider involved), this is likely reproducible from a stock install with no custom bridge required: just ask any multi-step reasoning question in the TUI with model_reasoning_summary = "detailed" set and compare against codex exec --json on the same prompt.

Happy to provide the full codex-tui.log files (three separate captures, increasing log verbosity each time) if useful. I trimmed the excerpts above for length.

View original on GitHub ↗