Skip to content

win: cancel pending cooked tty read when setRawMode(true) follows a synchronous mode bounce - #30732

Open
robobun wants to merge 1 commit into
mainfrom
farm/24a46a94/win-tty-cancel-cooked-read
Open

robobun wants to merge 1 commit into
mainfrom
farm/24a46a94/win-tty-cancel-cooked-read

Conversation

@robobun

@robobun robobun commented May 14, 2026 •

Copy link
Copy Markdown
Collaborator

What

On Windows, process.stdin.setRawMode(true) now reliably cancels an in-flight cooked ReadConsoleW on the libuv stdin reader thread after a prior synchronous setRawMode(false); setRawMode(true) bounce. Without this, the next raw-mode keystroke is consumed by the stuck cooked read and silently dropped.

Repro (Windows, real terminal)

process.stdin.setRawMode(true);
process.stdin.on('data', d => console.log('got', [...d]));
await new Promise(r => setImmediate(r));

// First cycle, synchronous — primes the stale flag:
process.stdin.setRawMode(false);
process.stdin.setRawMode(true);
await new Promise(r => setImmediate(r));

// Second cycle — event loop queues a cooked ReadConsoleW in between:
process.stdin.setRawMode(false);
for (let i = 0; i < 20; i++) await new Promise(r => setImmediate(r));
process.stdin.setRawMode(true);
console.log('raw again; type a single char');
// before: first keystroke is swallowed (or nothing until Enter)
// after:  first keystroke arrives immediately

This is the Ink reattach scenario: App unmount (setRawMode(false)) → remount (setRawMode(true)), repeated.

Root cause

This is a libuv bug in src/win/tty.c, present both before and after the Rust port (the Rust side — Source__setRawModeStdin in src/io/source.rs — correctly routes to uv_tty_set_mode on the shared process-global stdin_tty handle; the reader's uv_read_start uses that same handle).

uv__tty_read_stop() chose its cancellation path from handle->tty.rd.mode.mode:

if (uv__is_raw_tty_mode(handle->tty.rd.mode.mode)) {
  /* cancel raw read: write a FOCUS_EVENT */
} else if (!(handle->flags & UV_HANDLE_CANCELLATION_PENDING)) {
  /* cancel line read */
  uv__cancel_read_console(handle);
  handle->flags |= UV_HANDLE_CANCELLATION_PENDING;
}

But uv_tty_set_mode() updates mode.mode between its read_stop/read_start pair. When called twice back-to-back in one tick, the second read_stop runs against the same still-pending raw request (the loop hasn't drained IOCP yet) but sees mode.mode == UV_TTY_MODE_NORMAL from the first call. It takes the line-read path, calls uv__cancel_read_console() (a no-op — there is no line read), and sets UV_HANDLE_CANCELLATION_PENDING.

That flag is only ever cleared in uv_process_tty_read_line_req(). Since the pending request is a raw request, it completes via uv_process_tty_read_raw_req(), which never touches the flag. It sticks.

On the next setRawMode(false) → (event loop runs, cooked ReadConsoleW is queued on a threadpool thread) → setRawMode(true), uv__tty_read_stop sees CANCELLATION_PENDING already set and skips uv__cancel_read_console entirely. The worker thread stays blocked in ReadConsoleW. The next keystroke is consumed by it, delivered through uv_process_tty_read_line_req, and dropped on the floor because CANCELLATION_PENDING is set.

Fix

Patch uv__tty_read_stop to dispatch on handle->tty.rd.read_line_buffer.len — the type of the pending request — instead of mode.mode. read_line_buffer.len > 0 iff a line read is pending; this is the same invariant uv__process_tty_read_req() already uses to route completions. A stale-mode read_stop against a raw request now takes the raw-cancel path (writes an extra harmless FOCUS_EVENT) and never spuriously sets CANCELLATION_PENDING.

Applied as patches/libuv/win-tty-read-stop-match-pending-req.patch; upstreamable to libuv/libuv as-is.

Relationship to #30288

#30288 fixed process.stdin.isRaw tracking so code that reads isRaw to decide whether to restore cooked mode no longer spuriously drops to cooked on Windows. This PR covers code that deliberately calls setRawMode(false) (Ink teardown, readline close(), any re-init path) and then re-enters raw mode — a path #30288 did not touch.

Notes on the single-cycle repro

A single synchronous setRawMode(false); setRawMode(true) does not hang on its own (traced end-to-end — the raw request completes, the event loop re-arms a raw read, and input flows). It primes the stale flag; one more false→yield→true cycle after that is what hangs. The report's minimal repro happens to produce both effects when the mode bounces more than once (Ink remount, readline re-open).

Second failure mode

The report also mentions a stall after setRawMode(false) + unref() with no listener where a later on('readable') doesn't fire until a terminal resize. That path also runs uv__tty_read_stop with mode.mode == Normal against a pending raw request (via setFlowing(false) → WindowsBufferedReader::stop_reading() → uv_read_stop), so it hits the same stale-flag bug and is covered by this fix. If a distinct symptom persists after this lands it is likely the Bun-level own()/disown() ref dance rather than libuv and should be filed separately.

Verification

Added a ConPTY-backed test in test/js/node/tty.test.ts that walks the exact two-cycle sequence and asserts the first post-bounce keystroke reaches the child without Enter. The test runs on all platforms; on POSIX it locks in the existing (already-correct) termios behaviour. The affected code is Windows-only (libuv src/win/tty.c), so — as with #30288 — the fail-before/pass-after delta is only observable on Windows CI.

bun bd test test/js/node/tty.test.ts
 5 pass
 0 fail

no test proof · iteration 11 · Platform-specific test-only change; deferring to CI.

@robobun

robobun commented May 14, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 12:12 PM PT - Aug 15th, 2026

🔄 @robobun, the build for your commit bc435d94 (Build #98348) was cancelled — waiting for the next build...

@robobun

robobun commented May 14, 2026 •

Copy link
Copy Markdown
Collaborator Author

Status: Rebased onto current main (bc435d94). All 16 Windows test shards green (8x Server 2019, 8x Windows 11 aarch64); tty.test.ts passing. Ready for review.

This is a Windows-only libuv patch (patches/libuv/win-tty-read-stop-match-pending-req.patch). The fail-before/pass-after delta is only observable on Windows lanes; on POSIX there is no reader thread and the test just locks in existing termios behaviour (same precedent as #30288).

Build #98348 (bc435d94): all Windows build jobs and all 16 Windows test shards passed (patch applies and compiles against oven-sh/libuv@89ee343; tty.test.ts green on both Windows targets). One unrelated red: test/js/web/timers/setInterval.test.js "doesn't leak memory" timed out on debian-13 x64-asan. libuv is enabled: cfg => cfg.windows, so this diff compiles nothing differently on Linux; the test is not failing on recent main builds, so it is a flake rather than a main break. Remaining annotations are retried-and-passed flakes on unrelated files. Separately, the five darwin 14 aarch64 test-bun shards never ran: four expired waiting for a macOS agent and the last was canceled, which is why the build badge reads canceled rather than failed (same agent-availability pattern as build #54436; not a test failure, and libuv is not compiled on macOS). Final tally: 176/177 finished jobs passed. Re-roll was already spent earlier in this PR, so I am not pushing another retrigger.

Rebase notes: main bumped LIBUV_COMMIT to 89ee343 and added win-poll-abort-with-disconnect.patch. Verified against the new src/win/tty.c: uv__tty_read_stop still dispatches on mode.mode (bug present), uv__process_tty_read_req still dispatches on read_line_buffer.len (invariant holds), patch hunk offset regenerated (1078 -> 1093), git apply --check passes. Earlier rebase: #33527 added a test in the same describe block; this PR's test follows it. bun bd test test/js/node/tty.test.ts: 8 pass, 0 fail.


Why fix the dispatch in uv__tty_read_stop rather than clearing CANCELLATION_PENDING in uv_process_tty_read_raw_req: the defect is that read_stop picks a cancel strategy for a request that does not exist. Clearing the flag downstream would hide the symptom but read_stop would still call uv__cancel_read_console against a raw request, taking uv_tty_output_lock and flipping the file-static uv__read_console_status to TRAP_REQUESTED for no reason. Dispatching on the actual pending request means the wrong path is never entered, so there is no stale state to clean up. It also fixes the mirror case for free (line read pending, mode already flipped to raw still takes the line-cancel path).

Why a patch file rather than landing in the fork first: same route the two existing patches/libuv/*.patch took. This can be folded into oven-sh/libuv and the patch dropped on the next LIBUV_COMMIT bump; happy to open that PR if preferred.

On the read_line_buffer.len invariant: it is the discriminator uv__process_tty_read_req() itself uses (tty.c:1042). uv__tty_queue_read_raw sets read_line_buffer = uv_null_buf_ (len 0); uv__tty_queue_read_line fills it via alloc_cb (len > 0) and returns early without setting READ_PENDING if alloc yields len 0; uv_process_tty_read_line_req resets it to uv_null_buf_ on completion. So under READ_PENDING, len > 0 means a line request is in flight and len == 0 means a raw one is.

Prior CI: #73609 (54f66dcf) all Windows lanes green, tty.test.ts passing everywhere; #54393 (a2891639) Windows 11 aarch64 passed with the fix, Server 2019 exposed a ConPTY v1 limitation now handled by test.skipIf on builds < 19041.

@coderabbitai

coderabbitai Bot commented May 14, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Updates Windows TTY cancellation to choose the cancel path based on the pending read type, registers the libuv patch in the build, and adds a Windows regression test exercising setRawMode toggling around a pending read.

Changes

Windows TTY read-stop cancellation fix and regression test

Layer / File(s) Summary
Windows TTY read-stop cancellation dispatch fix
patches/libuv/win-tty-read-stop-match-pending-req.patch
uv__tty_read_stop() dispatch is changed to check pending request state (read_line_buffer.len == 0) instead of current mode state, with a comment describing the failure when mode toggles during a pending read.
Patch integration into libuv build configuration
scripts/build/deps/libuv.ts
Adds win-tty-read-stop-match-pending-req.patch to the libuv dependency patches array and documents the tty read cancellation mismatch.
Regression test for setRawMode toggling behavior
test/js/node/tty.test.ts
New Windows-focused test spawns a child that bounces raw mode around a pending read and asserts a raw keystroke is delivered to the child's data handler.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the Windows TTY cancellation fix for the synchronous raw-mode bounce.
Description check ✅ Passed The description explains the problem, root cause, fix, affected scenario, regression test, and verification details, although its headings differ from the template.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/js/node/tty.test.ts`:
- Around line 184-187: The test currently force-kills the child immediately
after key detection which can mask child failures; instead, after awaiting
Promise.race([gotKey.promise, eof.promise]) stop calling proc.kill() there, wait
for the process to exit by awaiting proc.exited, then close
proc.terminal?.close(), and only assert the child's exit code at the end; update
the sequence around gotKey.promise/eof.promise, proc.kill(), proc.exited, and
proc.terminal?.close() so exit is awaited and asserted last.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b9c98ad0-0187-4458-b962-9d4d689d2479

📥 Commits

Reviewing files that changed from the base of the PR and between 63035b3 and 540d0a0.

📒 Files selected for processing (3)
  • patches/libuv/win-tty-read-stop-match-pending-req.patch
  • scripts/build/deps/libuv.ts
  • test/js/node/tty.test.ts

Comment thread test/js/node/tty.test.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
test/js/node/tty.test.ts (1)

184-187: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Wait for the child to exit and assert the exit code last.

Force-killing here can hide a child-side failure after the KEY write. Capture const exitCode = await proc.exited;, close the terminal after that, and assert expect(exitCode).toBe(0) after the output assertions.

Proposed fix
-    await Promise.race([gotKey.promise, eof.promise]);
-    proc.kill();
-    await proc.exited;
+    await Promise.race([gotKey.promise, eof.promise]);
+    const exitCode = await proc.exited;
     proc.terminal?.close();
     output += decoder.decode();
@@
     const bytes = JSON.parse(match[1]);
     expect(bytes).toContain("x".charCodeAt(0));
+    expect(exitCode).toBe(0);
   });
As per coding guidelines, “When spawning processes in tests, expect stdout before expecting exit code for more useful error messages” and “Assert the exit code last in tests.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/js/node/tty.test.ts` around lines 184 - 187, The test currently
force-kills the child and closes the terminal before awaiting its exit, which
can hide failures; replace that sequence by awaiting and capturing the exit code
(const exitCode = await proc.exited), then close proc.terminal after awaiting
exit, and finally assert expect(exitCode).toBe(0) after all output/assertions
(use the existing gotKey.promise and eof.promise waits before checking
exitCode); update the teardown to remove proc.kill() so the process exits
normally and ensure proc.terminal?.close() runs after exit is awaited.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@test/js/node/tty.test.ts`:
- Around line 184-187: The test currently force-kills the child and closes the
terminal before awaiting its exit, which can hide failures; replace that
sequence by awaiting and capturing the exit code (const exitCode = await
proc.exited), then close proc.terminal after awaiting exit, and finally assert
expect(exitCode).toBe(0) after all output/assertions (use the existing
gotKey.promise and eof.promise waits before checking exitCode); update the
teardown to remove proc.kill() so the process exits normally and ensure
proc.terminal?.close() runs after exit is awaited.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 44b8adae-b084-431c-8efa-c903f0e4e063

📥 Commits

Reviewing files that changed from the base of the PR and between 540d0a0 and a289163.

📒 Files selected for processing (1)
  • test/js/node/tty.test.ts

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't find any issues and the analysis is thorough, but this patches libuv's Windows TTY read-cancellation dispatch — a vendored-dependency change whose correctness hinges on the read_line_buffer.len invariant, so it's worth a human sign-off from someone familiar with libuv's win/tty state machine.

Extended reasoning...

Overview

This PR adds a one-line behavioral patch to vendored libuv (src/win/tty.c), changing uv__tty_read_stop() to dispatch its cancellation path on handle->tty.rd.read_line_buffer.len == 0 instead of uv__is_raw_tty_mode(handle->tty.rd.mode.mode). The patch is registered in scripts/build/deps/libuv.ts (mechanical array append), and a ConPTY-backed regression test is added to test/js/node/tty.test.ts following the existing sibling-test pattern from #30288.

Security risks

None identified. The change is Windows-only TTY input-cancellation routing inside libuv; it does not touch auth, crypto, network, filesystem permissions, or any user-controllable parsing path. The worst plausible failure mode is mis-routed read cancellation (hung/dropped stdin), not a security exposure.

Level of scrutiny

This warrants human review despite its small surface area. It modifies a vendored core dependency in a code path that governs how Windows console reads are cancelled — effectively platform-level I/O plumbing for every Bun process with a TTY stdin on Windows. The fix's correctness rests on the invariant "read_line_buffer.len > 0 iff the pending request is a line read," which the PR claims (plausibly) is the same invariant uv__process_tty_read_req() already dispatches on. That claim is well-argued and the root-cause trace is detailed, but verifying it requires libuv-internals knowledge that goes beyond what I'd auto-approve. The patch is also described as upstreamable but has not yet been reviewed by libuv maintainers.

Other factors

  • Windows CI is green on all lanes per the build-status comment; the only failure is an unrelated Linux OOM flake in v8-heap-snapshot.test.ts.
  • The new test is appropriately skipped on ConPTY v1 (Server 2019 / build < 19041) with a clear rationale, and follows the established Bun.Terminal test pattern in the same file.
  • CodeRabbit's only comment (exit-code assertion ordering) was reasonably rebutted by the author and retracted.
  • The test relies on a 50-iteration setImmediate propagation window before sending a sentinel keystroke; this is event-loop-turn-based rather than wall-clock so it should be deterministic, but ConPTY input propagation is an out-of-process IPC hop, so there is some residual flake risk worth a human eye.
  • No CODEOWNERS concerns surfaced, but vendored-dep patches in this repo have historically gotten explicit maintainer review.

@robobun
robobun force-pushed the farm/24a46a94/win-tty-cancel-cooked-read branch from 9a76de2 to 54f66dc Compare July 16, 2026 03:55

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't find any bugs, but this patches vendored libuv's Windows TTY cancellation path — a subtle threading/state-machine change in a critical I/O layer — so it warrants a human look from someone familiar with the Windows event loop.

What was reviewed:

  • The read_line_buffer.len invariant matches how uv__process_tty_read_req already routes completions, and uv__tty_queue_read_raw/_line set it consistently — the dispatch key looks sound.
  • Checked that a stale-mode raw-cancel against a raw request is harmless (extra FOCUS_EVENT write, no flag mutation).
  • The ConPTY test follows the sibling updates isRaw pattern; the sentinel-'z' fallback keeps the failure mode a clean assertion rather than a hang, and the Server 2019 skip is scoped to the harness limitation only.
Extended reasoning...

Overview

This PR adds a one-line patch to vendored libuv (src/win/tty.c) changing uv__tty_read_stop to dispatch its cancellation path on handle->tty.rd.read_line_buffer.len == 0 instead of uv__is_raw_tty_mode(handle->tty.rd.mode.mode). It registers the patch in scripts/build/deps/libuv.ts and adds a ConPTY-backed regression test in test/js/node/tty.test.ts. The root-cause analysis is unusually thorough: a synchronous setRawMode(false); setRawMode(true) bounce leaves UV_HANDLE_CANCELLATION_PENDING stuck (only uv_process_tty_read_line_req clears it, but the pending req is raw), so the next real cooked-read cancel is skipped and ReadConsoleW blocks until Enter.

Security risks

None identified. This is Windows-only console read cancellation; no untrusted input parsing, auth, crypto, or network surface is touched.

Level of scrutiny

High. This is a patch to a vendored third-party dependency in the Windows event loop's stdin reader-thread cancellation logic — exactly the kind of concurrent state machine where a subtle mistake produces rare hangs or lost input. The libuv source is fetched at build time (github-archive), so the patch cannot be locally verified against the tree in this checkout, and the fail-before/pass-after delta is only observable on Windows CI. The invariant relied on (read_line_buffer.len > 0 iff a line read is pending) is well-argued and matches libuv's own completion-routing discriminator, but a maintainer who owns the Windows I/O layer should confirm it holds across all paths (including the alloc_cb-returns-zero early return in uv__tty_queue_read_line).

Other factors

The test is well-constructed (awaits observable conditions, wires eof to the race, uses a sentinel keystroke to convert the bug's hang into a deterministic assertion failure) and mirrors the existing ConPTY test pattern in the same file. The isConPTYv1 skip is documented and scoped. CodeRabbit's one nit was addressed/retracted. Prior CI runs on the pre-rebase branch showed all Windows lanes green. Still, per the approval guidelines, patches to critical platform I/O paths and vendored dependencies should not be auto-approved.

uv__tty_read_stop() picked its cancellation path from
handle->tty.rd.mode.mode. But uv_tty_set_mode() flips mode.mode between
its stop/start pair, so a synchronous setRawMode(false); setRawMode(true)
runs read_stop twice against the same still-pending raw request. The
second call sees mode=Normal, takes the line-read cancel path, and sets
UV_HANDLE_CANCELLATION_PENDING with no line request in flight. That flag
is only cleared in uv_process_tty_read_line_req, so it sticks.

On the next false -> (yield) -> true cycle where a real ReadConsoleW is
queued, read_stop sees the stale flag and skips uv__cancel_read_console.
The worker thread stays blocked; the next keystroke is consumed by
ReadConsoleW, routed through uv_process_tty_read_line_req, and dropped
because CANCELLATION_PENDING is set. Observed as frozen-until-Enter /
lost keystrokes after Ink App unmount/remount on Windows.

Fix: dispatch on read_line_buffer.len (the actual pending-request type),
the same invariant uv__process_tty_read_req already uses. A stale-mode
read_stop against a raw request now takes the raw cancel path (an extra
harmless FOCUS_EVENT) and never sets CANCELLATION_PENDING spuriously.

The ConPTY-backed regression test is skipped on Windows builds < 19041
(Server 2019 / 1809): ConPTY v1 does not reliably propagate raw
single-byte input after a cooked/raw mode bounce. The libuv fix still
ships for those builds; only the harness cannot observe it there.

Upstreamable to libuv/libuv as-is.
@robobun
robobun force-pushed the farm/24a46a94/win-tty-cancel-cooked-read branch from 54f66dc to bc435d9 Compare August 15, 2026 17:01

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed this PR and didn't find any bugs. Because it patches vendored libuv's Windows console-read cancellation path (threadpool + UV_HANDLE_CANCELLATION_PENDING state), a human sign-off on the vendored-dep change would still be worthwhile.

What was reviewed:

  • The read_line_buffer.len == 0 invariant — confirmed it matches how uv__process_tty_read_req already routes completions, and that uv__tty_queue_read_raw sets uv_null_buf_ while uv__tty_queue_read_line early-returns without READ_PENDING on len==0, so the discriminator is sound.
  • The reverse case (line read pending, mode already flipped to raw) — takes the line-cancel path as before, no regression there.
  • The new ConPTY test — bounded poll with a sentinel fallback rather than a bare sleep; eof is wired to every race so a child crash surfaces via the KEY-regex throw instead of hanging; follows the same shape as the sibling updates isRaw test.
Extended reasoning...

Overview

This PR adds a one-line patch to vendored libuv (src/win/tty.c), registers it in scripts/build/deps/libuv.ts, and adds a ConPTY-backed regression test to test/js/node/tty.test.ts. The patch changes uv__tty_read_stop() to select its cancellation strategy based on handle->tty.rd.read_line_buffer.len (the type of the pending request) instead of handle->tty.rd.mode.mode (the current mode, which uv_tty_set_mode mutates between its stop/start calls). The failure mode — a stale UV_HANDLE_CANCELLATION_PENDING flag causing a later cooked ReadConsoleW cancel to be skipped — is traced end-to-end in the PR description and patch comment.

Security risks

None. This is Windows-only console input handling; no auth, crypto, network, or untrusted-input parsing is touched. The change affects which of two existing cancellation paths runs, not what either does.

Level of scrutiny

Higher than the diff size suggests. Per the repo's landing-PRs guidance, changes under vendor/ and patches to vendored deps warrant the situational Dependencies & vendoring review. The affected code manages threadpool worker cancellation and a handle-state flag that gates future cancels — getting the discriminator wrong in the other direction (raw-cancel path against a real line read) would leave a threadpool worker stuck in ReadConsoleW. The invariant chosen (read_line_buffer.len) is the same one uv__process_tty_read_req already uses to route completions, and the PR explains why it holds (raw queue sets uv_null_buf_; line queue fills via alloc_cb and early-returns without READ_PENDING if len==0; line-req completion resets to uv_null_buf_). That reasoning checks out, but a maintainer familiar with the oven-sh/libuv fork should confirm this is the right layer for the fix vs. clearing CANCELLATION_PENDING in uv_process_tty_read_raw_req, and whether it should land in the fork instead of a patch file.

Other factors

  • Windows CI is green on all lanes per the robobun status; the two remaining reds (test-net-connect-memleak.js, require-cache.test.ts) are unrelated main-branch flakes on non-Windows lanes where libuv isn't even compiled (enabled: cfg => cfg.windows).
  • The test uses a 50-iteration setImmediate poll with a sentinel-write fallback rather than a bare sleep, which satisfies the "await the condition, don't sleep" rule; every wait is raced against eof so a child crash produces a descriptive throw rather than a timeout.
  • The one CodeRabbit comment (assert exit code) was addressed with a reasonable explanation matching the sibling test's pattern and marked resolved.
  • The isConPTYv1 skip carries a specific rationale (ConPTY v1 input propagation on build < 19041) and doesn't skip the fix itself, only the harness's ability to observe it.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant