TUI exits on one Ctrl+C when /side cleanup fails during startup

Open 💬 0 comments Opened Jul 15, 2026 by woan-lufei

Summary

A single Ctrl+C can exit the entire Codex TUI instead of returning to the parent thread when it is pressed while a /side conversation is still starting and the parent turn is still running.

Environment

  • LazyCodex version: OmO 4.18.1 installed, but the failing routing and side lifecycle are upstream Codex TUI code
  • Codex version: codex-cli 0.144.3 (the same relevant code is present in rust-v0.144.4 and current main at 1bbdb32789e1f79932df44941236ea3658f6e965)
  • OS: Windows 11 24H2, build 26100, x86_64
  • Install method: npm global package @openai/codex
  • Relevant config: multiple MCP/plugin servers make side startup observable; reproduction used PowerShell through Windows ConPTY with --no-alt-screen

Repository Decision

  • Target repository: openai/codex
  • Why this belongs there: /side, side-thread cleanup, key dispatch, and the global quit fallback are implemented in codex-rs/tui.
  • LazyCodex evidence (runtime + $LAZYCODEX_SOURCE_ROOT/lazycodex-source): latest LazyCodex source at 098177c52a4fc989cc1f8ac6fb3f94e330fb63d3 does not own the TUI /side key route. Plugins only make the startup window easier to hit.
  • Upstream Codex source evidence from $LAZYCODEX_SOURCE_ROOT/openai-codex-source: codex-rs/tui/src/app/input.rs calls maybe_return_from_side before normal key handling, but codex-rs/tui/src/app/side.rs returns false after side cleanup fails or leaves the side active. The same key then reaches the immediate global quit path in codex-rs/tui/src/chatwidget/interaction.rs.

Reproduction

  1. Start Codex 0.144.3 in PowerShell: codex --no-alt-screen.
  2. Start a parent turn that remains active, for example one that runs a 30-second command.
  3. While the parent turn is running, enter /side.
  4. While the side conversation is still starting (the repro showed Starting MCP servers ...), press Ctrl+C once.

Expected Behavior

The key should be consumed by the side-conversation return path. Codex should return to the active parent thread. If side cleanup fails, the TUI should remain open and show the cleanup error.

Actual Behavior

The TUI displays Shutting down..., exits with status 0, and prints a codex resume <session-id> command. One Ctrl+C exits the whole Codex process.

Evidence

  • Reproduced in an isolated temporary workspace on Codex 0.144.3.
  • Captured terminal state immediately before the keypress: Side from main thread · Ctrl+C to return while side startup was active.
  • Captured terminal state after one keypress: Shutting down....
  • Captured process result: exit code 0 and To continue this session, run codex resume <session-id>.
  • Control cases with an idle parent returned normally; the failure requires the parent turn to remain active while the side conversation is starting.
  • No matching open issue was found. #25499 reports the opposite symptom. Closed #20264 and PR #20282 fixed a related Ctrl+D fallthrough but do not cover cleanup failure.

Root Cause

The side return shortcut is only consumed when maybe_return_from_side(...) returns true:

  • codex-rs/tui/src/app/input.rs:139-143 forwards the key when that function returns false.
  • codex-rs/tui/src/app/side.rs:329-349 returns false when the switch/cleanup path leaves the side thread active.
  • codex-rs/tui/src/app/side.rs:364-445,527-554 can keep the side visible when interrupt/unsubscribe cleanup fails during startup.
  • codex-rs/tui/src/chatwidget/interaction.rs:388-399 then handles the same Ctrl+C as a global quit because DOUBLE_PRESS_QUIT_SHORTCUT_ENABLED is false.

This produces a double-action from one keypress: a failed side return followed by a full-process quit.

Proposed Fix

When side_return_shortcut_matches(key_event) is true and an active side parent exists, consume the key regardless of whether cleanup succeeds. Attempt the return, surface any cleanup error, and keep the TUI open instead of forwarding that same key to global quit handling.

Add an app-level regression test where the parent turn remains active, the side thread is in startup, side cleanup fails or remains active, and one Ctrl+C must not emit AppEvent::Exit.

Verification Plan

  • Re-run the reproduction above with the parent turn active and press one Ctrl+C during side startup.
  • Verify the TUI returns to the parent, or remains open with a cleanup error, but never displays Shutting down....
  • Verify Ctrl+C outside /side retains its current quit/interrupt behavior.
  • Verify normal idle /side return and Ctrl+D side return continue to work.

---
This issue or PR was generated by LazyCodex.
Tag: lazycodex-generated

View original on GitHub ↗