Failed to branch before the selected prompt: the selected prompt is a steer and cannot be branched independently

Open 💬 2 comments Opened Aug 16, 2026 by dat-lequoc
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

What version of Codex CLI is running?

0.147.0

What subscription do you have?

Plus

Which model were you using?

gpt-5.6-sol

What platform is your computer?

_No response_

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

_No response_

Codex doctor report

What issue are you seeing?

■ Conversation interrupted - tell the model what to do differently. Something went wrong? Hit /feedback to report the issue.
■ Failed to branch before the selected prompt: the selected prompt is a steer and cannot be branched independently

What steps can reproduce the bug?

Steer it, then interrupt (Esc); Esc x2 to go back to modify the steer prompt => impossible

What is the expected behavior?

_No response_

Additional information

_No response_

View original on GitHub ↗

2 Comments

github-actions[bot] contributor · 11 days ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #38210

Powered by Codex Action

jdcodes1 · 11 days ago

Traced on main @ 1f41cc5d92 — this is a guaranteed dead-end built from two components that disagree about what's selectable.

The mismatch. The Esc-Esc backtrack picker enumerates every visible user message in the transcript as a selection target — it walks transcript_cells and accepts any UserHistoryCell, with no notion of whether the message was a turn's initial prompt or a mid-turn steer:

https://github.com/openai/codex/blob/1f41cc5d92722748e45cae9cecc6d883a4e7cbb1/codex-rs/tui/src/app_backtrack.rs#L468-L499

But the fork resolver hard-rejects steers, by design ("app-server cannot fork in the middle of a turn"):

https://github.com/openai/codex/blob/1f41cc5d92722748e45cae9cecc6d883a4e7cbb1/codex-rs/tui/src/app_backtrack.rs#L500-L560

So the selectable set and the forkable set differ, and your exact flow — steer, Esc to interrupt, Esc-Esc (which lands on the most recent user message, i.e. the steer) — always hits the gap. It's not an edge case; it's the default landing position after steering.

Two coherent fixes, not mutually exclusive:

  1. Make the picker honest: skip steer messages when stepping through backtrack targets (or render them non-selectable). Requires propagating the is-steer bit into the transcript cell/projection, which backtrack_fork_before_turn_id already computes (user_messages_in_turn > 0). This removes the error but doesn't serve your actual intent — you wanted to modify the steer.
  2. Graceful fallback for steers: when the selection is a steer, fork before the turn (the same turn.id the initial-prompt path returns — forking at a turn boundary is exactly what app-server supports) and prefill the composer with the steer text. The turn's initial prompt gets dropped along with the steer, so the UI should say so — e.g. prefill and hint "branched before the turn; re-send the initial prompt if you still need it", or prefill both messages queued. That turns the dead-end into the workflow you were reaching for: back up, edit the steer, resubmit.

One adjacent trap worth covering in the same fix: the resolver also bails when the turn is still InProgress (#L560-L562). Since this flow starts from an interrupt, whether the interrupted turn has been persisted as completed by the time Esc-Esc resolves is a race — if the interrupt hasn't landed yet, the user gets the "still in progress" variant of the same dead-end. A regression test that steers, interrupts, and immediately backtracks would cover both paths.