Skip to content

fix(core): stop ratatui cursor queries from racing the TUI event stream - #36318

Merged
FrozenPandaz merged 2 commits into
masterfrom
feature/nxc-4597-error-insert_before-failed-when-swapping-to-inline-mode
Jul 17, 2026
Merged

fix(core): stop ratatui cursor queries from racing the TUI event stream#36318
FrozenPandaz merged 2 commits into
masterfrom
feature/nxc-4597-error-insert_before-failed-when-swapping-to-inline-mode

Conversation

@AgentEnder

Copy link
Copy Markdown
Member

Current Behavior

Since the ratatui 0.30 bump, running tasks in the inline TUI intermittently logs ERROR insert_before failed - method may not exist on this terminal type, typically noticed when swapping to inline mode.

Root cause: ratatui-core 0.1.2 (a semver-compatible patch that arrived via a later Cargo.lock refresh, not the 0.30 bump commit itself) added a cursor-position snapshot to Terminal::clear():

pub fn clear(&mut self) -> Result<(), B::Error> {
    let original_cursor = self.backend.get_cursor_position()?; // new in 0.1.2
    self.clear_viewport()?;
    self.backend.set_cursor_position(original_cursor)?;
    ...

insert_before (the inline scrollback path) calls clear() on every insert, so every scrollback flush now writes ESC[6n and reads the reply from terminal input — while crossterm's EventStream owns terminal input. When the query loses that race it times out (~2s) and insert_before returns Err. This is the same query/event-stream conflict the TUI already works around with draw_without_autoresize and by stopping the event stream around mode switches; ratatui-core 0.1.2 re-introduced it from inside the render path where we can't stop the stream.

(Enabling ratatui's scrolling-regions feature was considered and rejected: with our full-height inline viewport it pushes lines to scrollback via CSI S in a 1-row DECSTBM region, which xterm.js/VSCode drops instead of saving to scrollback.)

Expected Behavior

No cursor-position query can ever run on the TUI render path. The crossterm backend is wrapped in CursorCachingBackend, whose get_cursor_position answers from the last position set through the backend instead of touching the terminal. This is sound because the TUI keeps the cursor hidden and positions it absolutely, and the inline viewport is full-height, so ratatui's inline viewport math yields the same result regardless of the reported position. This also structurally covers other ratatui internals that query the cursor (e.g. fullscreen autoresizeresizeclear() on terminal resize).

The error log for a failed scrollback insert now includes the actual io::Error instead of the speculative "method may not exist on this terminal type" message.

Validation: cargo test -p nx --lib passes (477 tests, includes a new unit test for the cached-cursor behavior); cargo check/clippy introduce no new warnings.

Related Issue(s)

Fixes NXC-4597

ratatui-core 0.1.2 added a cursor-position snapshot to Terminal::clear,
which insert_before calls on every inline scrollback insert. The query
writes ESC[6n and reads the reply from terminal input, racing the live
crossterm EventStream and intermittently timing out, surfacing as
'insert_before failed' when running in inline mode.

Wrap the crossterm backend so get_cursor_position answers from a cached
position (last set through the backend) instead of touching the
terminal. The TUI keeps the cursor hidden and positions it absolutely,
and the inline viewport is full-height, so ratatui's viewport math is
unaffected by the cached value.
The previous message asserted insert_before 'may not exist on this
terminal type', which is not a real failure mode and discarded the
underlying io::Error that would have pointed at the root cause.
@AgentEnder
AgentEnder requested a review from a team as a code owner July 13, 2026 16:25
@AgentEnder
AgentEnder requested a review from MaxKless July 13, 2026 16:25
@netlify

netlify Bot commented Jul 13, 2026

Copy link
Copy Markdown

Deploy Preview for nx-docs ready!

Name Link
🔨 Latest commit f163af1
🔍 Latest deploy log https://app.netlify.com/projects/nx-docs/deploys/6a551177476b520008a498a8
😎 Deploy Preview https://deploy-preview-36318--nx-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Jul 13, 2026

Copy link
Copy Markdown

Deploy Preview for nx-dev ready!

Name Link
🔨 Latest commit f163af1
🔍 Latest deploy log https://app.netlify.com/projects/nx-dev/deploys/6a551177b5d8520008738eca
😎 Deploy Preview https://deploy-preview-36318--nx-dev.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@nx-cloud

nx-cloud Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

View your CI Pipeline Execution ↗ for commit f163af1

Command Status Duration Result
nx affected --targets=lint,test,build,e2e,e2e-c... ✅ Succeeded 38m 16s View ↗
nx run-many -t check-imports check-lock-files c... ✅ Succeeded 4s View ↗
nx-cloud record -- pnpm nx-cloud conformance:check ✅ Succeeded 1m 4s View ↗
nx build workspace-plugin ✅ Succeeded <1s View ↗
nx-cloud record -- nx sync:check ✅ Succeeded 18s View ↗
nx-cloud record -- nx format:check ✅ Succeeded <1s View ↗

☁️ Nx Cloud last updated this comment at 2026-07-13 17:08:42 UTC

@FrozenPandaz
FrozenPandaz merged commit af78b8d into master Jul 17, 2026
25 checks passed
@FrozenPandaz
FrozenPandaz deleted the feature/nxc-4597-error-insert_before-failed-when-swapping-to-inline-mode branch July 17, 2026 22:13
FrozenPandaz pushed a commit that referenced this pull request Jul 20, 2026
…am (#36318)

## Current Behavior

Since the ratatui 0.30 bump, running tasks in the inline TUI
intermittently logs `ERROR insert_before failed - method may not exist
on this terminal type`, typically noticed when swapping to inline mode.

Root cause: ratatui-core **0.1.2** (a semver-compatible patch that
arrived via a later `Cargo.lock` refresh, not the 0.30 bump commit
itself) added a cursor-position snapshot to `Terminal::clear()`:

```rust
pub fn clear(&mut self) -> Result<(), B::Error> {
    let original_cursor = self.backend.get_cursor_position()?; // new in 0.1.2
    self.clear_viewport()?;
    self.backend.set_cursor_position(original_cursor)?;
    ...
```

`insert_before` (the inline scrollback path) calls `clear()` on every
insert, so every scrollback flush now writes `ESC[6n` and reads the
reply from terminal input — while crossterm's `EventStream` owns
terminal input. When the query loses that race it times out (~2s) and
`insert_before` returns `Err`. This is the same query/event-stream
conflict the TUI already works around with `draw_without_autoresize` and
by stopping the event stream around mode switches; ratatui-core 0.1.2
re-introduced it from inside the render path where we can't stop the
stream.

(Enabling ratatui's `scrolling-regions` feature was considered and
rejected: with our full-height inline viewport it pushes lines to
scrollback via `CSI S` in a 1-row DECSTBM region, which xterm.js/VSCode
drops instead of saving to scrollback.)

## Expected Behavior

No cursor-position query can ever run on the TUI render path. The
crossterm backend is wrapped in `CursorCachingBackend`, whose
`get_cursor_position` answers from the last position set through the
backend instead of touching the terminal. This is sound because the TUI
keeps the cursor hidden and positions it absolutely, and the inline
viewport is full-height, so ratatui's inline viewport math yields the
same result regardless of the reported position. This also structurally
covers other ratatui internals that query the cursor (e.g. fullscreen
`autoresize` → `resize` → `clear()` on terminal resize).

The error log for a failed scrollback insert now includes the actual
`io::Error` instead of the speculative "method may not exist on this
terminal type" message.

Validation: `cargo test -p nx --lib` passes (477 tests, includes a new
unit test for the cached-cursor behavior); `cargo check`/`clippy`
introduce no new warnings.

## Related Issue(s)

Fixes NXC-4597

(cherry picked from commit af78b8d)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants