Repository navigation
node 10.2.0+ turning off stty echo when using process.stdin.setRawMode() #21020
Description
Activity
- addedttyIssues and PRs related to the tty subsystem.Issues and PRs related to the tty subsystem.
on May 29, 2018 Thanks for reporting this! Do you have any reproduction that doesn’t involve extra setup, or alternatively a standalone reproduction that you could share somewhere?
just run
node -e "process.stdin.setRawMode(true)"in bashReacted by Anna HenningsenI thought it happened when I ran
process.stdin.unref(), but it seems that all you need isprocess.stdin.setRawMode(true)Here this shows the full behavior:
bash-4.4$ node -v; node -e "process.stdin.setRawMode(true)"; stty v10.1.0 speed 9600 baud; lflags: -icanon -isig -iexten echoe echoke echoctl iflags: -icrnl -ixon iutf8 -brkint oflags: -oxtabs cflags: cs8 -parenb bash-4.4$ n 10.2.0 bash-4.4$ node -v; node -e "process.stdin.setRawMode(true)"; stty v10.2.0 speed 9600 baud; lflags: -icanon -isig -iexten -echo echoe echoke echoctl iflags: -icrnl -ixon iutf8 -brkint oflags: -oxtabs cflags: cs8 -parenb- changed the title
[-]node 10.2.0+ turning off stty echo[/-][+]node 10.2.0+ turning off stty echo when using process.stdin.setRawMode()[/+]on May 29, 2018 - addedconfirmed-bugIssues and PRs for confirmed bugs.Issues and PRs for confirmed bugs.
on May 29, 2018 what a shame! seems you worked hard on that PR!
I did. 😄 I don’t think we need a full revert here, but addressing this properly is not exactly trivial either. The quick-and-probably-good-enough solution to this problem would be this:
--- a/src/node.cc +++ b/src/node.cc @@ -4289,6 +4289,7 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data, WaitForInspectorDisconnect(&env); env.set_can_call_into_js(false); + uv_tty_reset_mode(); env.RunCleanup(); RunAtExit(&env);
It does address the problem locally for me, and I think I’ll open a PR with it in a bit.
13 remaining items
- added a commit that references this issue
on Jun 29, 2018 - added a commit that references this issue
on Aug 16, 2018 - added a commit that references this issue
on May 24, 2019 - added a commit that references this issue
on Jun 13, 2019 - added a commit that references this issue
on Jun 17, 2019 - added a commit that references this issue
on Apr 16, 2025 - added a commit that references this issue
on Jul 27, 2026 just run
node -e "process.stdin.setRawMode(true)"in bash@jdx (OP), @addaleax (confirmed as a bug), and @bnoordhuis ("fixed" this issue in #24260)
Why was this reported/confirmed/fixed as a bug?
We have an application which knowingly changes the terminal to raw node, and the bug is that node does what it's requested to do? How is that a bug?
What if that application or module is
stty.js- which is an interface to the systemstty, and all it does is change the terminal settings, because the user needs to change it? like changing the baud rate, or any other terminal settings?Why should node revert/override (on exit) any changes which the application does knowingly to the terminal?
The reason I'm asking, other than our hypothetical
stty.js, is that a solution which restores the terminal state unconditionally on exit (#24260 which "fixes" this issue) fails to take other use cases into account, likenode -h | pager(e.g.less).Now,
node -h(orjs-beautifypiped into less which is the original report to "less") is non interactive, doesn't care about the terminal state, and certainly doesn't need or try to change it - it simply writes stuff to stdout, which happens to be a pipe into "less".But the pager is interactive. The pager sets raw mode during init because it owns the terminal and requires it to interact with the user. But once it dequeued all the output from node, and node exits, node resets its stdin and stderr (not stdout - because that's not a tty) to their initial states. Since this stdin is the same controlling terminal which "less" uses (as
/dev/tty- because it's stdin is the pipe from node), this reset changes the terminal settings while less is running, and results in unexpected behavior in "less".Whether it does or doesn't end up as issue depends on a race condition of when exactly node records the terminal state on init, and when exactly "less" changes the tty to raw mode.
If "less" does that first, then node will see an initial state of raw mode on init, and "restoring" this state on exit won't actually change the settings.
But if node records the state before less changes it to raw mode, then this reset by node would break less once node exits.
In the past we had good luck and less happened to set raw mode before node reads the initial state. But a recent change in "less" of the init timing moved this race to the "bad case", and now any pipe from node into less (and presumably other pagers too) breaks less when node exits.
For reference:
- The original report in "less": In new version, for some input, keys need Enter gwsw/less#834
- Reported again in node: Problem with newer
less#66440 - Fix attempt PR in node: src: add --no-restore-terminal-state #66540
- The original PR which added restore-on-exit in 2018: src: restore stdio on program exit #24260
The discussion currently happens at the node report at #66440 which tries to assess whether PR #66540 is the best solution, and whether there are better solutions.
Here's what we've know/concluded so far:
- Restoring O_NONBLOCK on exit by node is orthogonal to this tty reset issue. I.e. it doesn't change the tty state, and therefore doesn't contribute to the tty state reset issue. I.e. it can stay.
- Node doesn't guarantee (at the docs) to restore the terminal state on exit. Therefore it should be the responsibility of an application which changes the mode to restore it - if it wants (our
stty.jsintentionally needs to keep it modified on exit, but maybe other interactive application which set raw mode do need to restore it on exit). - We're not sure exactly why node needs to restore the tty state on exit when the application does knowingly
process.stdin.setRawMode(true). This question brought us to this issue (node 10.2.0+ turning off stty echo when using process.stdin.setRawMode() #21020) as the original reason to reset raw mode on exit. - We're not sure why node also resets stdout/stderr on exit. So far we couldn't find evidence that this is required. src: restore stdio on program exit #24260 claims to fix the stdin raw "issue", but it also resets stdout/stderr - without mentioning why, as far as I can tell.
I think that ideally, at #66440, we would reach a conclusion that none of stdin/stdout/stderr should be reset by node on exit, but for now we have too many questions (mainly those listed above) which we don't have absolute answers to.
As the people who know the history of this reset better than us, we'd appreciate if you could join the discussion at #66440.
Thanks.
PS.
The credit for the investigation at the node side goes to @fatihguzeldev (which also opened the attempted fix at #66540).I'm occasionally involved with "less", and trying to help here too with this issue, if I can.
Starting with node 10.2.0 it is behaving as if I'm running
stty -echoand hides the echo output when usingheroku runand typing characters in. Node 10.3.0 and 10.3.1 also have the issue, but 10.1.0 and lower does not.Right now I'm working on providing a simpler example that does not involve the Heroku CLI. I'm assuming this is something related to using
.setRawMode()but will update when I have more information.Update: Here is a simpler example:
node -e "process.stdin.setRawMode(true)"in bash. It setsstty -echo.If you run this in node 10.1.0, nothing happens. If you run it in 10.2.0 in bash, it will turn echo off.