Skip to content

node 10.2.0+ turning off stty echo when using process.stdin.setRawMode() #21020

Description

@jdx
  • Version: 10.2.0
  • Platform: MacOS
  • Subsystem: Unsure right now

Starting with node 10.2.0 it is behaving as if I'm running stty -echo and hides the echo output when using heroku run and 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 sets stty -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.

Activity

  1. added
    ttyIssues and PRs related to the tty subsystem.
    on May 29, 2018
  2. addaleax commented on May 29, 2018

    @addaleax
    Member

    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?

  3. jdx commented on May 29, 2018

    @jdx
    ContributorAuthor

    just run node -e "process.stdin.setRawMode(true)" in bash

  4. jdx commented on May 29, 2018

    @jdx
    ContributorAuthor

    I thought it happened when I ran process.stdin.unref(), but it seems that all you need is process.stdin.setRawMode(true)

  5. jdx commented on May 29, 2018

    @jdx
    ContributorAuthor

    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
    
  6. 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
  7. addaleax commented on May 29, 2018

    @addaleax
    Member

    This was caused by my 17e289e (#19377). My initial guess would be that closing the libuv handle for stdin closes the corresponding file descriptor and leaves libuv unable to reset the state for that file descriptor in uv_tty_reset_mode().

  8. jdx commented on May 29, 2018

    @jdx
    ContributorAuthor

    what a shame! seems you worked hard on that PR!

  9. addaleax commented on May 29, 2018

    @addaleax
    Member

    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.

  10. 13 remaining items

  11. added a commit that references this issue on Jun 29, 2018
  12. added a commit that references this issue on Aug 16, 2018
  13. added a commit that references this issue on May 24, 2019
  14. added a commit that references this issue on Jun 13, 2019
  15. added a commit that references this issue on Jun 17, 2019
  16. added a commit that references this issue on Jul 27, 2026
  17. avih commented on Oct 8, 2026

    @avih

    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 system stty, 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, like node -h | pager (e.g. less).

    Now, node -h (or js-beautify piped 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 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.js intentionally 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.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    confirmed-bugIssues and PRs for confirmed bugs.ttyIssues and PRs related to the tty subsystem.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions