Cursor jumps and flashes when deleting text in input field
Open 💬 7 comments Opened Apr 3, 2026 by Dioman-Keita
💡 Likely answer: A maintainer (github-actions[bot], contributor)
responded on this thread — see the highlighted reply below.
What version of Codex CLI is running?
codex-cli 0.118.0
What subscription do you have?
free
Which model were you using?
gpt-5.4 medium
What platform is your computer?
Microsoft Windows NT 10.0.26200.0 x64
What terminal emulator and version are you using (if applicable)?
CMD, Antigravity terminal
What issue are you seeing?
When deleting characters in the input field, the cursor frequently jumps or changes position very rapidly. This creates a flashing effect in the UI. The issue also occurs occasionally while typing, but it is most noticeable during deletions.
What steps can reproduce the bug?
Uploaded thread: 019d3b53-4440-7a03-9fd0-013b0e9f485d
It is very difficult to capture a screenshot of this behavior because the flashing happens so quickly when deleting.
What is the expected behavior?
The cursor should consistently remain at the logical insertion point during both typing and deletion, without flashing or unexpected movement.
Additional information
_No response_
7 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Are you able to repro the problem outside of the Antigravity integrated terminal? If not, this is likely a problem in Antigravity, and you should report the problem to its maintainers.
I was able to reproduce the problem in VSCode’s integrated terminal (Version 2.6.22, x64). However, it works correctly in CMD (Microsoft Windows [Version 10.0.26200.8037], Windows NT 10.0.26200.0 x64). I initially thought it didn’t, but after testing again, the issue does not occur there.
For reference, I am using Codex CLI version 0.118.0.
@etraut-openai , thanks for tracking this.
I’ve reproduced this and prepared a branch that fixes it:
https://github.com/snowyukitty/codex/tree/fix-tui-cursor-sync-frame
I can’t open an upstream PR from this account because GitHub rejected
CreatePullRequestonopenai/codex.If you want, I can keep the patch details here or open the PR once invited.
Validation:
just fmtjust test -p codex-tuijust fix -p codex-tui?2026l, with nothing after the sync-end marker.If this direction looks useful, I’m happy to share the patch details here or open the PR once invited.
Thanks for maintaining Codex. I hope this is helpful.
Setting the cursor position before showing the cursor and hiding the cursor before the scrollback insertion fixed the issue for me on Windows (using alacritty).
@noaho04's two-part diagnosis holds up, and only one of the two parts has landed.
#40166 fixed the second half:
try_draw_with_sizenow positions the caret beforeshowing it, so a single draw no longer exposes it at its previous position. The first
half — hiding the caret before the scrollback insertion — is still missing, and it is
the half that covers everything a frame writes before that draw.
Why the remaining half matters. A frame does not reach the terminal as one write.
The backend writes through
std::io::stdout(), which is aLineWriterwith a 1 KiBbuffer that also flushes on every newline, and history insertion emits
Print("\r\n")per line. So a frame that pushes N history rows crosses the write boundary at least N
times, plus once per 1 KiB of diff. A terminal that implements synchronized updates
(DEC private mode 2026) holds those chunks until the frame ends. A terminal that does
not paints each chunk as it arrives, and every chunk ends with the caret wherever the
last
MoveToleft it — the footer, then the status line, then the composer.That mechanism also explains why this and #17823 look like different bugs: xterm.js only
implemented mode 2026 in 6.0.0 (xtermjs/xterm.js#5453), so Tabby and Wave discard the
markers outright. Worth saying plainly, though: hiding the caret does not depend on
knowing which terminals honour mode 2026, or on how completely they honour it. A caret
that is not visible cannot be painted at the wrong position by anything.
The change. Hide the caret at the top of both synchronized frames in
tui.rs.Nothing has to remember to bring it back, and the reason is worth stating precisely,
because I got it slightly wrong the first time. It is not that every frame ends in a
draw — a frame can fail partway. It is that
try_draw_with_sizewrites a caretdecision in both of its arms, and that
Terminal::hide_cursorandTerminal::show_cursorwrite unconditionally rather than short-circuiting onhidden_cursor. So a frame that reaches its draw ends with the caret in the state thatframe asked for, whatever preceded it. A frame that does not reach its draw failed
writing to stdout; the caret stays hidden until the next frame that succeeds, or until
restore_commonandDrop for Terminalrestore it at teardown.One commit on current
main, one line per frame plus two tests.Tuiis hard-wired toCrosstermBackend<Stdout>, so those two lines cannot themselves be pointed at a capturebackend without generifying it. The tests instead pin the two properties that make them
correct: a caret hidden before a frame's raw writes is taken back over by the draw and
becomes visible exactly once, after the final position; and both visibility writes reach
the terminal even when the flag already agrees, since the restore path writes
Showstraight to stdout behind
Terminal's back.Validation on
x86_64-pc-windows-msvc, rustc 1.95.0:cargo test -p codex-tui --lib— 3738 passed, 3 failed. The three are environmental,expecting
~\AppData\...where this machine reportsC:\Users\...; unmodifiedmainreads 3736 passed, 3 failed with the same three.
cargo clippy -p codex-tui --all-targets --all-features -- -D warnings— clean.I cannot open a PR —
openai/codexonly accepts them from collaborators — so thebranch is here for anyone who can carry it.
Follow-up to my last comment here, which offered the caret hide with the caveat that it had no direct test.
It has one now.
vt100implements no synchronized-output mode, so a frame drawn against it is presented piece by piece — the same reason the caret is visible mid-frame in the terminals these two issues report. Driving a realTui::drawinto avt100screen behind aLineWriterand sampling the caret at each write boundary shows, on today'smain, one frame painting a visible caret in four different places; with the caret hidden for the frame, one.Branch, two commits on
2df6705423: https://github.com/snowyukitty/codex/tree/fix/tui-caret-frame-regression-testThe first commit is the fix alone and stands on its own; the second is the test plus a defaulted backend parameter on
Tuithat changes no call sites. The details and the caveats are on #17823 rather than repeated here.@noaho04 — you reported that positioning before showing and hiding before the scrollback insertion together fixed this on alacritty. #40166 landed the first; this is the second, now with a trace showing what it removes.