-
Notifications
You must be signed in to change notification settings - Fork 29.8k
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
lib: add case of no argument to ERR_INVALID_ARG_VALUE in errors.js #21262
Conversation
ERR_INVALID_ARG_VALUE is an error for wrong arguments given to the function. But calling a function without argument should also generate a sensible error message. For no argument case, parameter 'value' should be passed with zero value and the error message is generated. In readSync(fd, buffer, offset, length, position), triggers validateOffsetLengthRead() in lib/internal/fs/utils.js for validation. When buffer is empty, a weird message is generated "The value of offset is out of range.It must be >= 0 && <= 0. Received 0". There should be a special case when buffer is empty or bufferLength is zero, which should trigger ERR_INVALID_ARG_VALUE error. Fixes: nodejs#21193
Hi, welcome and thank you for the contribution! Unfortunately looking at the issue closely I don't think my suggestion in #21193 (comment) is valid. The proper fix should be adding a validation for the buffer before each call of Replacing every call of if (buffer.length === 0) {
throw ERR_INVALID_ARG_VALUE('buffer', buffer, 'is empty and can\'t be written');
}
validateOffsetLengthRead(offset, length, buffer.length); would be more appropriate. Can you apply this fix instead? |
lib/internal/fs/utils.js
Outdated
if (offset < 0 || offset >= bufferLength) { | ||
if (bufferLength === 0) { | ||
err = new ERR_INVALID_ARG_VALUE('buffer', 0, | ||
"is empty and can't be written."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By the way, in this code base we use single quotes. Please run make lint
(non-Windows) or vcbuild.bat lint
(Windows) to run the tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please also move this statement in the already existing if statement and change it to an ERR_OUT_OF_RANGE
error. That way the extra check is only triggered in the error case as in:
if (offset < 0 || offset >= bufferLength) {
if (bufferLength === 0) {
err = new ERR_OUT_OF_RANGE('bufferLength', '> 0', bufferLength);
} else {
err = new ...;
}
} else ...
} | ||
); | ||
} | ||
// { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, we do not leave the code commented in the code base.
// ); | ||
// } | ||
|
||
assert.expectsError(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you write the tests in the corresponding test/parallel/test-fs-read*.js
files? Or add a new test for the new error and name it with something like test/parallel/test-fs-read-empty-buffer.js
.
lib/internal/fs/utils.js
Outdated
if (offset < 0 || offset >= bufferLength) { | ||
if (bufferLength === 0) { | ||
err = new ERR_INVALID_ARG_VALUE('buffer', 0, | ||
"is empty and can't be written."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please also move this statement in the already existing if statement and change it to an ERR_OUT_OF_RANGE
error. That way the extra check is only triggered in the error case as in:
if (offset < 0 || offset >= bufferLength) {
if (bufferLength === 0) {
err = new ERR_OUT_OF_RANGE('bufferLength', '> 0', bufferLength);
} else {
err = new ...;
}
} else ...
lib/internal/errors.js
Outdated
@@ -637,6 +637,8 @@ E('ERR_INVALID_ARG_VALUE', (name, value, reason = 'is invalid') => { | |||
let inspected = util.inspect(value); | |||
if (inspected.length > 128) { | |||
inspected = `${inspected.slice(0, 128)}...`; | |||
} else if (inspected === '0') { | |||
inspected = 'No value.'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure I understand this change.
ERR_INVALID_ARG_VALUE is an error for wrong arguments given to the function. But calling a function without argument should also generate a sensible error message. For no argument case, parameter 'value' should be passed with zero value and the error message is generated. In readSync(fd, buffer, offset, length, position), triggers validateOffsetLengthRead() in lib/internal/fs/utils.js for validation. When buffer is empty, a weird message is generated "The value of offset is out of range.It must be >= 0 && <= 0. Received 0". There should be a special case when buffer is empty or bufferLength is zero, which should trigger ERR_INVALID_ARG_VALUE error. Fixes: nodejs#21193
lib/internal/fs/utils.js
Outdated
@@ -293,7 +293,13 @@ function validateOffsetLengthRead(offset, length, bufferLength) { | |||
let err; | |||
|
|||
if (offset < 0 || offset >= bufferLength) { | |||
err = new ERR_OUT_OF_RANGE('offset', `>= 0 && <= ${bufferLength}`, offset); | |||
if (bufferLength === 0) { | |||
throw ERR_INVALID_ARG_VALUE('buffer', 0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please change this to an ERR_OUT_OF_RANGE
error that corresponds to the bufferLength
. I do not know what buffer
should stand for in this case and invalid argument value errors are normally used in different contexts. Here it should definitely be an out of range error as I also suggested in my code example in my earlier comment.
In validateOffsetLengthRead(), an error message is generated if offset or length are out of range. There should also be an error message if buffer is empty, in which case it cannot write data in it. Generally this validateOffsetLengthRead() is triggered with fs.readSync(fd, buffer, offset, length, position), where if buffer is empty, the case for zero bufferLength is triggered and the corresponding message is thrown. Fixes: nodejs#21193
The change itself looks good. we have to add a test though to get this landed. Have a look at the other tests and try to add one. Then run |
@BridgeAR Note that the user does not pass bufferLength, it’s computed by us so the error in this patch is still confusing for users who pass an empty buffer to read a file into. |
In validateOffsetLengthRead(), an error message is generated if offset or length are out of range. But there should also be an error message if buffer is empty, when no data can be written on it. Generally, validateOffsetLengthRead() is triggered with fs.readSync(fd, buffer, offset, length, position), where if 'buffer' is empty, the case for zero bufferLength should be triggered and the error message for empty buffer should be thrown. Fixes: nodejs#21193
Updated @BridgeAR |
@BridgeAR @joyeecheung Is there still any discrepencies or changes to be made. If so, please suggest. I will be most happy to work them out. 🙂 |
Have you considered my suggestion in #21262 (comment) ? |
@joyeecheung AFAIK we normally only use the arguments of the currently executed function, no matter if it that argument is passed through directly or not. If we really want to say that the buffer itself is not OK (because the user passed that through as an argument), then an ERR_INVALID_ARG_VALUE would probably be best. The question for me is though if that is really less confusing. @AdityaSrivast sorry for the inconvenience here. Some times it is not trivial to really get the errors right and they need a couple iterations. So thanks a lot for sticking to it! 👍 |
@BridgeAR I prefer validating the Buffer itself since |
@joyeecheung I have tried using buffer.length before validateOffsetLengthRead() and while linting I got an error that 'buffer' is not defined. Even inside the function we cannot use buffer.length and it gives the same error. But I do think that bufferLength === 0 case is just the perfect option as with empty buffer, this part of code was getting executed: Here, the message which we were getting was: Regarding user friendliness, I feel that since the user is using readSync(fd, buffer, offset, length, position), there are arguments "offset" and "length", which give meaningful error messages for "offset" or "length" being out of the range. But "bufferLength" is not an argument in readSync(), which the user uses. So, it may result into slight confusion for users. @BridgeAR Thanks for acknowledging. It's my pleasure contributing to node and nodejs. 😄 |
That's weird, I can lint with this diff just fine. diff --git a/lib/fs.js b/lib/fs.js
index f80bb6ac33..b5f8e4d3a2 100644
--- a/lib/fs.js
+++ b/lib/fs.js
@@ -47,6 +47,7 @@ const { Buffer, kMaxLength } = require('buffer');
const errors = require('internal/errors');
const {
ERR_FS_FILE_TOO_LARGE,
+ ERR_INVALID_ARG_VALUE,
ERR_INVALID_ARG_TYPE,
ERR_INVALID_CALLBACK
} = errors.codes;
@@ -457,6 +458,14 @@ function read(fd, buffer, offset, length, position, callback) {
});
}
+ if (buffer.length === 0) {
+ throw ERR_INVALID_ARG_VALUE(
+ 'buffer',
+ buffer,
+ 'is empty and cannot be written'
+ );
+ }
+
validateOffsetLengthRead(offset, length, buffer.length);
if (!Number.isSafeInteger(position))
@@ -487,6 +496,14 @@ function readSync(fd, buffer, offset, length, position) {
return 0;
}
+ if (buffer.length === 0) {
+ throw ERR_INVALID_ARG_VALUE(
+ 'buffer',
+ buffer,
+ 'is empty and cannot be written'
+ );
+ }
+
validateOffsetLengthRead(offset, length, buffer.length);
if (!Number.isSafeInteger(position))
diff --git a/lib/internal/fs/promises.js b/lib/internal/fs/promises.js
index 750d0d7579..2d1fa8818a 100644
--- a/lib/internal/fs/promises.js
+++ b/lib/internal/fs/promises.js
@@ -12,6 +12,7 @@ const { Buffer, kMaxLength } = require('buffer');
const {
ERR_FS_FILE_TOO_LARGE,
ERR_INVALID_ARG_TYPE,
+ ERR_INVALID_ARG_VALUE,
ERR_METHOD_NOT_IMPLEMENTED
} = require('internal/errors').codes;
const { getPathFromURL } = require('internal/url');
@@ -207,6 +208,13 @@ async function read(handle, buffer, offset, length, position) {
if (length === 0)
return { bytesRead: length, buffer };
+ if (buffer.length === 0) {
+ throw ERR_INVALID_ARG_VALUE(
+ 'buffer',
+ buffer,
+ 'is empty and cannot be written'
+ );
+ }
validateOffsetLengthRead(offset, length, buffer.length);
if (!Number.isSafeInteger(position)) If you call something like |
@joyeecheung Thanks a lot for correcting. Actually, I was applying the fix in utils.js before validateOffsetLengthRead();, which was accordingly throwing the error. But this fix works. I will apply it. |
While using read() or readSync() function, it should be verified that the arguments of the function are in proper range and hold legitimate values, for a successful read process. For this validateOffsetLengthRead() function is called, which gives an error message for offset and length passed by the user, if not in order. But there should also be an error when an empty buffer is passed as argument, when it cannot be written over. The empty buffer case should be checked before calling validateOffsetLengthRead() and ERR_INVALID_ARG_VALUE should be thrown corresponding to the empty buffer argument in read and readSync function. Fixes: nodejs#21193
@joyeecheung @BridgeAR Updated |
@joyeecheung Did I miss out something, please? 😕 |
@AdityaSrivast Sorry, missed the notification.
I use visual studio code so I just use the search tab there with |
const fixtures = require('../common/fixtures'); | ||
const assert = require('assert'); | ||
const fs = require('fs'); | ||
const filepath = fixtures.path('testcodes.txt'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This and the additional fixtures should be unnecessary, we can use any existing fixtures for this test, like fixtures.path('elipses.txt')
assert.throws( | ||
() => fs.readSync(fd, buffer, 0, 10, 0), | ||
{ | ||
code: 'ERR_INVALID_ARG_VALUE', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you validate the message? Something like
message: 'The argument \'buffer\' is empty and cannot be written. ' +
'Received <Buffer >'`
should work (or whatever the actual error message is)
While using read() or readSync() function, it should be verified that the arguments of the function are in proper range and hold legitimate values, for a successful read process. For this validateOffsetLengthRead() function is called, which gives an error message for offset and length passed by the user, if not in order. But there should also be an error when an empty buffer is passed as argument, when it cannot be written over. The empty buffer case should be checked before calling validateOffsetLengthRead() and ERR_INVALID_ARG_VALUE should be thrown corresponding to the empty buffer argument in read and readSync function. Fixes: nodejs#21193
@joyeecheung Is there still anything left out, please? |
I'd think this is semver-patch because personally I don't think changing the code of a validation error (ERR_OUT_OF_RANGE -> ERR_INVALID_ARG_VALUE) is worth a semver-major. @jasnell WDYT? |
@joyeecheung AFAIK the intention of the error code was to be able to change the error message without it being semver major but changes to the code would still be semver major. I personally believe we should start adding error groups as they would be far more helpful for most users than the actual error codes as quite a few errors belong all in one category. If we'd have those, I would argue it would not be semver major anymore because an |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This LGTM but I do think this is a semver-major
It's undoubtedly a great feeling contributing to nodejs and node for me. @joyeecheung when can my commit be merged, please? 😄 |
New CI: https://ci.nodejs.org/job/node-test-pull-request/15808/
This PR should be ready to land if the CI is green. Sorry, there are no bots here taking care of landing PRs automatically and most of the people who have commit access here are flooded by notifications so if you want to push things forward please ping us by our GitHub handle - people usually respond faster if you do that.
I don't have any more good first issues off the top of my head, maybe watching https://github.com/nodejs/node/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22 would help? |
@joyeecheung The CI is green. Can you please merge this PR? |
Landed in 42bded8, thanks! |
@joyeecheung Thanks a lot, means a lot. ❤️ |
Fixes: nodejs#21193 PR-URL: nodejs#21262 Reviewed-By: Joyee Cheung <[email protected]> Reviewed-By: James M Snell <[email protected]>
ERR_INVALID_ARG_VALUE is an error for wrong arguments given to the
function. But calling a function without argument should also generate
a sensible error message. For no argument case, parameter 'value'
should be passed with zero value and the error message is generated.
In readSync(fd, buffer, offset, length, position), triggers
validateOffsetLengthRead() in lib/internal/fs/utils.js for validation.
When buffer is empty, a weird message is generated "The value of offset
is out of range.It must be >= 0 && <= 0. Received 0". There should be a
special case when buffer is empty or bufferLength is zero, which should
trigger ERR_INVALID_ARG_VALUE error.
Fixes: #21193
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passes