Skip to content

Signal Terminal end-of-input at stdin EOF instead of hanging - #6676

Merged
tim-smart merged 4 commits into
Effect-TS:mainfrom
chenxin-yan:fix/node-terminal-stdin-eof
Jul 28, 2026
Merged

Signal Terminal end-of-input at stdin EOF instead of hanging#6676
tim-smart merged 4 commits into
Effect-TS:mainfrom
chenxin-yan:fix/node-terminal-stdin-eof

Conversation

@chenxin-yan

@chenxin-yan chenxin-yan commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Closes #4895

Problem

NodeTerminal never observes stdin end-of-input:

  • readInput ends its Queue<UserInput, Cause.Done> only when shouldQuit matches a Ctrl+C/Ctrl+D keypress. With piped or closed stdin (echo y | my-cli login), any prompt after the buffered keypresses are consumed blocks on Queue.take forever — even though Prompt.run already maps queue end to QuitError and documents that it fails "if terminal input ends".
  • readLine only listens for the readline "line" event, so it hangs the same way at EOF (the original report in Terminal.readLine hangs when input stream is closed #4895).

Fix

  • make tracks end-of-input once per terminal (stdin "end" fires once per process, and Bun never sets readableEnded, so readers created after the event cannot re-detect it from the stream).
  • readInput ends its queue on stdin "end" (or immediately when input already ended). Keypresses buffered before the end still deliver first; Prompt.run then fails with Terminal.QuitError through the existing Cause.Done mapping.
  • readLine fails with Terminal.QuitError when the readline interface closes. Listening to readline "close" rather than stdin "end" preserves the existing behavior where a final line without a trailing newline still flushes through "line" first.

Interactive TTY sessions are unaffected: a TTY stdin does not emit "end" during normal use, and Ctrl+C/Ctrl+D in raw mode remain keypresses handled by shouldQuit. BunTerminal reuses this implementation, so both runtimes are covered.

Verification

Verified end-to-end in a downstream CLI (Bun runtime) that previously shipped a wrapper Terminal layer implementing exactly this queue-end behavior: with this change applied to the installed package and the wrapper removed, its full CLI test suite passes, including printf 'y\n' | cli prompt flows and prompt interruption via closed stdin.

Happy to add a spawned-fixture test for the piped-stdin case if you'd like one — I didn't see existing NodeTerminal coverage to extend.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed terminal behavior when standard input reaches end-of-file.
    • Terminal key input and line-reading now end cleanly with Terminal.QuitError instead of hanging after input is closed or piped.
    • readLine now fails immediately for readers created after input has already ended.
    • Improved listener cleanup so terminal handlers are reliably removed when reads complete.
  • Tests
    • Added end-to-end coverage for EOF, queued input delivery, and unterminated line handling.

@github-project-automation github-project-automation Bot moved this to Discussion Ongoing in PR Backlog Jul 27, 2026
@changeset-bot

changeset-bot Bot commented Jul 27, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d0d11de

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3e22e5fc-d5b5-47d7-9419-65692a190d89

📥 Commits

Reviewing files that changed from the base of the PR and between 9138f01 and d0d11de.

📒 Files selected for processing (1)
  • packages/platform-node-shared/src/NodeTerminal.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/platform-node-shared/src/NodeTerminal.ts

📝 Walkthrough

Walkthrough

NodeTerminal tracks stdin EOF, closes its input queue when stdin ends, and causes readLine to fail with Terminal.QuitError. Tests cover prompt, queued-input, and unterminated-line behavior, with a changeset documenting the patch release.

Changes

Node terminal EOF handling

Layer / File(s) Summary
Track and propagate stdin EOF
packages/platform-node-shared/src/NodeTerminal.ts
Tracks stdin completion, ends the input queue immediately or on the end event, and removes registered listeners during cleanup.
Fail line reads and validate EOF behavior
packages/platform-node-shared/src/NodeTerminal.ts, packages/platform-node-shared/test/NodeTerminal.test.ts, packages/platform-node-shared/test/fixtures/node-terminal.ts, .changeset/node-terminal-stdin-eof.md
readLine fails with Terminal.QuitError after input closes; spawned fixture scenarios validate prompt, buffered-input, and unterminated-line handling, and the changeset records the patch release.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR makes readLine fail on closed stdin with QuitError and preserves buffered input, matching issue #4895.
Out of Scope Changes check ✅ Passed The changes are limited to the NodeTerminal fix, its tests, and a changeset, with no unrelated scope detected.

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

Address review polish on the stdin EOF fix:

- Reduce the changeset to a single-line summary instead of a paragraph
  restating the implementation.
- Drop the `=== true` coercion on `stdin.readableEnded`; both uses are
  boolean contexts, so it never changed the result.
- Collapse the `readInput` end-of-input comment to the reason it exists
  (consumers would hang) rather than narrating what the handler does.

Comments explaining the Bun `readableEnded` gap and why `readLine`
listens to readline "close" instead of stdin "end" are kept, since
neither is evident from the code.
@tim-smart
tim-smart enabled auto-merge (squash) July 28, 2026 02:14
@tim-smart
tim-smart merged commit 381c141 into Effect-TS:main Jul 28, 2026
15 checks passed
@github-project-automation github-project-automation Bot moved this from Discussion Ongoing to Done in PR Backlog Jul 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Bundle Size Analysis

File Name Current Size Previous Size Difference
basic.ts 6.63 KB 6.63 KB 0.00 KB (0.00%)
batching.ts 9.37 KB 9.37 KB 0.00 KB (0.00%)
brand.ts 6.26 KB 6.26 KB 0.00 KB (0.00%)
cache.ts 10.12 KB 10.12 KB 0.00 KB (0.00%)
config.ts 19.22 KB 19.22 KB 0.00 KB (0.00%)
differ.ts 18.42 KB 18.42 KB 0.00 KB (0.00%)
http-client.ts 20.76 KB 20.76 KB 0.00 KB (0.00%)
logger.ts 10.28 KB 10.28 KB 0.00 KB (0.00%)
metric.ts 8.55 KB 8.55 KB 0.00 KB (0.00%)
optic.ts 7.41 KB 7.41 KB 0.00 KB (0.00%)
pubsub.ts 14.20 KB 14.20 KB 0.00 KB (0.00%)
queue.ts 11.09 KB 11.09 KB 0.00 KB (0.00%)
schedule.ts 10.27 KB 10.27 KB 0.00 KB (0.00%)
schema-class.ts 18.16 KB 18.16 KB 0.00 KB (0.00%)
schema-fromJsonSchemaDocument.ts 28.02 KB 28.02 KB 0.00 KB (0.00%)
schema-representation-roundtrip.ts 24.34 KB 24.34 KB 0.00 KB (0.00%)
schema-string-transformation.ts 12.69 KB 12.69 KB 0.00 KB (0.00%)
schema-string.ts 10.35 KB 10.35 KB 0.00 KB (0.00%)
schema-template-literal.ts 14.49 KB 14.49 KB 0.00 KB (0.00%)
schema-toArbitraryLazy.ts 20.93 KB 20.93 KB 0.00 KB (0.00%)
schema-toCodeDocument.ts 23.38 KB 23.38 KB 0.00 KB (0.00%)
schema-toCodecJson.ts 18.32 KB 18.32 KB 0.00 KB (0.00%)
schema-toEquivalence.ts 18.00 KB 18.00 KB 0.00 KB (0.00%)
schema-toFormatter.ts 17.88 KB 17.88 KB 0.00 KB (0.00%)
schema-toJsonSchemaDocument.ts 21.52 KB 21.52 KB 0.00 KB (0.00%)
schema-toRepresentation.ts 18.56 KB 18.56 KB 0.00 KB (0.00%)
schema.ts 17.43 KB 17.43 KB 0.00 KB (0.00%)
stm.ts 12.05 KB 12.05 KB 0.00 KB (0.00%)
stream.ts 9.37 KB 9.37 KB 0.00 KB (0.00%)

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

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Terminal.readLine hangs when input stream is closed

2 participants