Repository navigation
readline with /dev/tty: echos and doesn't close #21319
Description
Activity
(lldb) bt * thread #10 * frame #0: 0x00007fff62f2214a libsystem_kernel.dylib`read + 10 frame #1: 0x000000010092f2b6 node`uv__fs_buf_iter + 78 frame #2: 0x000000010092cf29 node`uv__fs_work + 358 frame #3: 0x0000000100929755 node`worker + 95 frame #4: 0x00007fff630e8661 libsystem_pthread.dylib`_pthread_body + 340 frame #5: 0x00007fff630e850d libsystem_pthread.dylib`_pthread_start + 377 frame #6: 0x00007fff630e7bf9 libsystem_pthread.dylib`thread_start + 13 (lldb) thr sel 1 * thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP frame #0: 0x00007fff62f21bf2 libsystem_kernel.dylib`kevent + 10 libsystem_kernel.dylib`kevent: -> 0x7fff62f21bf2 <+10>: jae 0x7fff62f21bfc ; <+20> 0x7fff62f21bf4 <+12>: movq %rax, %rdi 0x7fff62f21bf7 <+15>: jmp 0x7fff62f17b00 ; cerror_nocancel 0x7fff62f21bfc <+20>: retq (lldb) bt * thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP * frame #0: 0x00007fff62f21bf2 libsystem_kernel.dylib`kevent + 10 frame #1: 0x000000010093abbb node`uv__io_poll + 836 frame #2: 0x000000010092b88d node`uv_run + 315 frame #3: 0x000000010003415c node`node::Start(v8::Isolate*, node::IsolateData*, int, char const* const*, int, char const* const*) + 780 frame #4: 0x0000000100033c23 node`node::Start(uv_loop_s*, int, char const* const*, int, char const* const*) + 442 frame #5: 0x00000001000333ba node`node::Start(int, char**) + 390 frame #6: 0x0000000100001034 node`start + 52 (lldb)
Basically the reader thread is engaged in reading from the device, and is blocked, immune to the stream close. This is closely related to #15439 and a documentation PR detailing the limitation is in progress through #21212
input.closeseem to be working in the code you provided but I don't know why it should, as the below code hangs too.var input = require('fs').createReadStream('/dev/tty'); setTimeout(() => { input.close() }, 1000)
pushing a null character into the device stream helps to unblock the reader thread.
@@ -8,6 +8,7 @@ var rl = readline.createInterface({ rl.question("This is the prompt: ", function (response) { console.log("This is the response:", response); rl.close(); + input.push(null); // If I don't close the input explicitly, it stays open. // input.close(); });
Hope this helps.
Thanks.
To your point, I found that the order in which readline and the input are closed seems to matter:
rl.question("This is the prompt: ", function (response) { console.log("This is the response:", response); // If I close the input first, it still stays open. input.close(); rl.close(); });
It may just be a goldilocks timing issue - or maybe the use of
terminal: falsechanges it too. I haven't tried every permutation.I'll try your
.push(null)and see how that works for me.Reacted by Gireesh Punathil and takegue@addaleax I'm not quite sure how that works. The docs talk a lot about
process.stdinandprocess.stdout, but they don't actually give any examples of how to usetty.Based on playing around in the repl I'm guessing I might use it by doing something like this?
var fd = fs.openSync('/dev/tty', 'r'); var input = tty.ReadStream(fd);
Could I somehow use
ttyon Windows too?Reacted by Julien tellier@coolaj86 Yes, exactly. :)
Could I somehow use
ttyon Windows too?I don’t know about that. @nodejs/platform-windows
I guess
fs.openSync('conin$', 'r')would be something similar on Windows, but it fails withENOENTvar fd = fs.openSync('/dev/tty', 'r'); var input = tty.ReadStream(fd);
This code seems correct to me, did that help @coolaj86?
Could I somehow use
ttyon Windows too?So long as you know the stdin handle, I think so.
- addedreadlineIssues and PRs related to the built-in readline module.Issues and PRs related to the built-in readline module.ttyIssues and PRs related to the tty subsystem.Issues and PRs related to the tty subsystem.
on Jun 15, 2018 @Fishrock123 Yes, that is the correct way to do it on Mac and Linux.
@gireeshpunathil I've tried a few different ways but can't tell if
input.push(null)has any affect.It seems like node is now properly exiting, but leaving
/dev/ttyin a bad state. The shell will just hang until I hit enter and then the bash script that was calling the node script continues.For now I just print "Press any key to continue..." before calling
process.exit(0)and then once enter is hit everything goes as expected.It seems like node is now properly exiting, but leaving /dev/tty in a bad state. The shell will just hang until I hit enter and then the bash script that was calling the node script continues.
You didn't mention how exactly you (or bash) start node, but if it's running in the background, then opening
/dev/ttywithout thefs.constants.O_NOCTTYflag turns it into a controlling terminal. That's unlikely to be what you want.I added
fs.constants.O_NOCTTYand it did not change the behavior.For context
I made a convenient curl | bash installer to bootstrap the install process of my app:
curl -fsSL https://get.example.app | bashWhen that script runs it installs an exact version of node (v10.2.1 presently as other versions we tested had particular bugs that broke the app) to a special prefix directory and (unzip / tar / gunzip)s the an exact version (tag) of the app from a git repo also into that same directory.
installer.sh:#!/bin/bash # do some stuff to install node # do some stuff to unzip / untar our app # generate a bash file to call the app /path/to/app/bin/app --init --some options /path/to/app/bin/app
The script generates and then calls another script which calls that version of node and the app, passing any options.
/path/to/bin/app:#!/bin/bash /path/to/app/bin/node /path/to/app/bin/app.js "$@"
Where it gets stuck
# This is where it needs some additional input from the user to finish the install /path/to/app/bin/app --init --some options # That hangs and it doesn't progress to here: /path/to/app/bin/appWe're now talking about 2 separate but related issues. I'll try to come up with a test case to show this second issue as well.
Hi, @coolaj86. I know this issue has been quiet for a very long time and it's possible you're not working on the relevant code base anymore. Which of the issues described here are you still experiencing (if any)?
github-actions commented
on Jun 27, 2026 on Jun 27, 2026 – with GitHub ActionsContributorMore actionsThis issue has been marked as stale due to 210 days of inactivity.
It will be automatically closed in 30 days if no further activity occurs. If this is still relevant, please leave a comment or update it to keep it open.- addedstaleIssues and PRs marked stale due to inactivity and scheduled for automatic closure.Issues and PRs marked stale due to inactivity and scheduled for automatic closure.
on Jun 27, 2026 github-actions commented
on Jul 28, 2026 on Jul 28, 2026 – with GitHub ActionsContributorMore actionsThis issue has been automatically closed after 30 days of inactivity following its stale status (no activity for a total of 120 days).
If this is still relevant, feel free to reopen it or leave a comment with additional details so we can continue the discussion.
The use case is that a node script is being run from a piped bash script and so
process.stdinis the pipe and/dev/ttymust be used explicitly to get user input.Output:
Uncommenting
terminal: falsesolves the issue of echoing, but creates a new problem in that it no longer treatsstdoutas a proper terminal.Past issues that appear to be similar: #7965 nodejs/node-v0.x-archive#7101 https://stackoverflow.com/questions/24661774/createinterface-prints-double-in-terminal