-
Notifications
You must be signed in to change notification settings - Fork 29.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fs.readSync throws EOF Error instead of returning 0 (Windows, if STDIN is a pipe) #35997
Comments
The stack trace of the error points at the following code: Lines 590 to 592 in c1da528
I basically expect the windows |
You will get much better results with: /// script.js
const fs = require('fs');
console.error('Node: ' + process.execPath);
console.error('Version: ' + process.version);
console.error('Platform: ' + process.platform);
console.error('process.stdin: ' + process.stdin.constructor.name);
const buffer = Buffer.alloc(4_096);
let read;
do {
read = fs.readFileSync('/dev/stdin', buffer, 0, buffer.length, null);
if (read != 0) {
console.log('STDIN: ' + buffer.slice(0, read).toString('utf-8'));
}
} while (read != 0);
console.log('EOF was (normally) reached.'); Don't fiddle with the 0, 1 and 2 file descriptors. They are open in non-blocking mode and libuv doesn't expect this when you are passing a file descriptor. |
Hey @mmomtchev,
This is interesting and all... But |
No, you are right, I can't think of any easy way to synchronously read from stdin on Windows But the real answer is that Node is built for async I/O |
We already have the ugly code to loop on The real thing I'm concerned with here, is how |
It says that the descriptor you are passing must be opened in blocking mode. |
Where though? I'm looking at: https://nodejs.org/api/fs.html#fs_fs_readsync_fd_buffer_offset_length_position, and the only place on this page where a requirement for In any case, in light of your confirming that we are doing things likely to trip |
@RomainMuller in fact, the stdin is not opened in non-blocking mode on Windows, that is the case only on Linux and OSX, so maybe there is a solution to your problem |
On Windows libuv does not properly handle pipes when operating in file mode. I wonder if it is supposed to, because normally, there is a separate pipe mode, but it is a trivial change. In any case, this issue should be taken to https://github.com/libuv/libuv This is everything that is needed to make it work:
|
But then again, you will end up with Windows-specific JS code, which is not the Node way |
@ronag is this supposed to work? |
Sorry, sync api's is not my area of expertise. |
It is not specific to synchronous mode. This is getting stranger by the minute. const fs = require('fs');
console.error('process.stdin: ' + process.stdin.constructor.name);
(async () => {
let read;
read = fs.createReadStream(null, { fd: 0 })
for await (const r of read)
console.log(r)
console.log('EOF was (normally) reached.')
})(); but this leaves it in blocking mode: const fs = require('fs');
(async () => {
let read;
read = fs.createReadStream(null, { fd: 0 })
for await (const r of read)
console.log(r)
console.log('EOF was (normally) reached.')
})(); The difference is accessing |
Easiest way to check if a file descriptor is in blocking or non-blocking mode is |
@RomainMuller, please disregard what I said, it seems that Node has a problem with handling consistently |
@joyeecheung, the type of If users are not supposed to be playing with 0, 1 and 2 file descriptors anymore, it should probably be mentioned, and maybe even add a warning |
So there are three separate issues
|
The 2nd bullet point there has two tracking issues for it already (libuv/libuv#2962 and libuv/libuv#2062) |
There are some examples I think have the same reason. $ echo test | node.exe -p 'require("fs").readFileSync(0)'
$ echo test | node.exe -e 'require("fs").readFile(0, console.log)' doesn't works: $ curl -s http://ifconfig.me/all.json | node.exe -p 'require("fs").readFileSync(0)'
<...>
Error: EOF: end of file, read
at Object.readSync (fs.js:578:3)
at tryReadSync (fs.js:353:20)
at Object.readFileSync (fs.js:390:19)
at [eval]:1:15
at Script.runInThisContext (vm.js:131:20)
at Object.runInThisContext (vm.js:297:38)
at Object.<anonymous> ([eval]-wrapper:10:26)
at Module._compile (internal/modules/cjs/loader.js:1118:30)
at evalScript (internal/process/execution.js:94:25)
at internal/main/eval_string.js:23:3 {
errno: -4095,
syscall: 'read',
code: 'EOF'
}
$ curl -s http://ifconfig.me/all.json | node.exe -e 'require("fs").readFile(0, console.log)'
<...>
[Error: EOF: end of file, read] {
errno: -4095,
code: 'EOF',
syscall: 'read'
} In all cases constructor name of stdin is Socket |
There are three separate issues here:
|
This is not really a design decision but a necessity. STDIO can be of different types and the operations on the file descriptors MUST match those cases. I stumbled upon this quite a lot myself. Check out https://www.npmjs.com/package/sonic-boom. This handles most of the edge cases related to use https://github.com/nodejs/node/blob/master/lib/internal/bootstrap/switches/is_main_thread.js contains all the logic that create a different implementation depending on the type of the file descriptors. Note that you can find a lot of deeply incorrect information on blog posts about Node.js.
Can you please articulate this? A PR would be welcomed. It's important that we lazy-load things to speed up the boostrap of Node.js.
This looks like a bug. PR? |
For example, currently, on Linux when
If the user is allowed to use FDs 0, 1 and 2, than yes, maybe. The problem is that when |
@mcollina , on Linux |
It's totally possible to handle
I'll be -1 to that. |
Ok, there is a solution to the non-blocking problem - I find it awful since it is essentially a way around the fundamentals of libuv and Node, but never mind, it is possible and it seems to be what people do - there is another example of this in my comments above In this case the only thing that needs solving is the pipe closing on Windows - if this is to be allowed - and in this case this is a libuv problem which should support reading from pipes in file mode - libuv/libuv#3043 |
@mcollina Just to make it clear - the official position on that one is that FD 0, 1 and 2 can be both blocking or non-blocking depending on the situation and the OS, and it is up to the user code to handle this? |
Yes. |
I don't remember what the original behavior was but it wasn't by design. I vaguely remember (could be wrong) that we used to turn them non-blocking from the start and allowing them to be blocking until you access |
@joyeecheung, the current solution when it comes to FDs 0, 1 and 2 is absolutely horrible - ie they tend to transparently switch from blocking to non-blocking, requiring the user to reimplement the async read loop in his code by eventually microsleeping... However me too (and I didn't take @mcollina word for granted, I spent some time reading/testing) I came to the conclusion that there is simply no other solution. I still think that Node should not incite people to write horrible code, so this should not be official 😄 , but jokes apart, people are using these FDs, and most of the time they don't even realize something very weird is going on. They only viable long-term solution I see is that libuv supports reading from regular files in non-blocking mode - something that would be totally useless except to give Node (and the user code) an uniform abstraction layer. When it comes to the lazy-loading - at this point it doesn't change anything to not do it - the user code will still have to expect these FDs to be in either mode. I will only try pushing a PR to libuv for the |
If the confusing part is that the FD switches from blocking to non-blocking once you access |
Yes, it is exactly what made that issue so confusing for me. Changing this however this will have a detrimental performance effect on process creation for everyone and it won't really accomplish anything - the user will still need to support both blocking and non-blocking FDs |
Changing it to non-blocking is often likely going to be problematic for any native C libraries that nodejs interacts with, as well as any programs that are execv from nodejs. Thus, I don't recommend doing that. It's also potentially quite problematic in the (very infrequent) case where some other external library calls |
Me too, as an old-school UNIX guy, I was initially appalled by the design decision to have a non-blocking stdio - it is something that is known to break things. But how do you implement pipes in libuv? They are currently built around |
the following patch appears to work (tested with piped curl) fs.readSync = function(origReadSync) {
return function newReadSync() {
try {
return origReadSync.apply(this, arguments);
} catch (e) {
if (e.code === 'EOF')
return 0;
else
throw e;
}
};
}(fs.readSync); |
@bughit it is a remarkably ugly solution, so I think it will be very appropriate to include it in the read loop with the microsleep 😄 |
The fs.readSync function throws EOF instead of returning 0 like in other systems. See nodejs/node#35997 for more information.
The fs.readSync function throws EOF instead of returning 0 like in other systems. See nodejs/node#35997 for more information.
* No await runner (#523) * cml-publish mime and repo (#519) * cml-publish mime and repo * cleanup * image/svg+xml * buffer * resolve * text buffer outputs plain * native bolean * snapshot * Fix pipe reading logic on Windows (#516) The fs.readSync function throws EOF instead of returning 0 like in other systems. See nodejs/node#35997 for more information. * minor doc fixes (#534) * cml-pr command (#510) * test-cml-pr * packages * pre-release * log isci * log isci * user_name * log gitlab url * log gitlab url * short 8 * len paths * no undef * git * files * files * files * files * files * files * files * files * files * files * files * files * files * files * files * files * files * sha2 * sha2 * log filepath * log filepath * log filepath * exists and clean * SHA vs SHA2 * pre-release * pre-release 2 * pre-release 3 * pre-release 3 * pre-release 3 * pre-release 3 * pre-release 3 * pre-release 3 * git back * git back * git back * release * REPO_TOKEN * html_url * cml pr defaults * cml pr defaults * test strict again * CI * vars * shorter params * snapshots and olivaw * test david * BB fix * snapshots * No git sha dependencies (#517) * check head_sha * 0.4.0 Co-authored-by: Helio Machado <0x2b3bfa0+git@googlemail.com> Co-authored-by: Casper da Costa-Luis <work@cdcl.ml>
Hi @RomainMuller, I wanted to look into this issue, but was not able to reproduce (tried 18.18.2, 20.10.0 and 22.0.0-pre). Do you think this can be closed now? |
Since this is no longer reproducible, and there has been no answer since the last comment, I'll close this. If you still experience the same issue, please reopen this issue or open a new one. |
fs
What steps will reproduce the bug?
Given the following node script:
Will behave differently based on how it is invoked:
How often does it reproduce? Is there a required condition?
This issue happens 100% of the time when
process.stdin
is aSocket
instance.What is the expected behavior?
The expected and documented behavior is that
fs.readSync
returns0
when EOF is reached.What do you see instead?
Instead, an
Error: EOF
is thrown, which is unexpected.Additional information
This problem was surfaced as users are starting to see failures on Windows at aws/aws-cdk#11314.
We have additionally seen issues where in similar conditions (
process.stdin
is a socket/pipe), attempting to performfs.readSync
on file descriptor0
(aka STDIN), occasionally results in unexpectedEAGAIN
errors being thrown, which might be related. That was highlighted in aws/aws-cdk#5187 and we are currently working with a pretty ugly workaround that would be nice if removed (but that other issue is a lot more difficult to steadily reproduce, as it does not always trigger, and may be caused by side-effects of another operation).The text was updated successfully, but these errors were encountered: