You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Error.name should not contain display elements #20253
I agree that adding the code to the name is weird. For me name is a programmatically-inspectable property, just like code, and not for "human consumption" like message. Having pretty formatting in there with a space and brackets goes against that.
Browser errors have no code property, they always use name, so that's where this intuition is coming from. For example, for fetch you would have to check if err.name === 'AbortError'. The only other option is using instanceof, but that is bad practice (assert on interfaces, not implementations).
Personally I see code as a subcategory of name, e.g. name can be TypeError and codeERR_SOCKET_BAD_TYPE. It's both useful to be able to make assertions about the broader category of and error as well as the exact error reason. For example, in an Express error handler, I might want to never return any TypeErrors to the client because they are programming errors. Or take p-retry as an example (emphasis mine):
Returns a Promise that is fulfilled when calling input returns a fulfilled promise. If calling input returns a rejected promise, input is called again until the max retries are reached, it then rejects with the last rejection reason. It doesn't retry on TypeError as that's a user error.
Checking code here wouldn't be helpful.
I would prefer if both name and code are for programmatic inspection without fancy formatting, and Error.prototype.toString() could be modified to include both error.name and error.code in the output, as toString()is unarguably purely for human consumption. Doing this for all errors, including user errors, would be a great feature and encourage users to make use of error codes too. Alternatively it could only be an internal base error class.
I just had a look at this and played around how this could be fixed and it turns out to be quite a difficult task at hand.
We use util.inspect for everything that we log and when inspecting errors, we return the error stack (plus enumerable properties on the error).
Manipulating the stack is not an option because the stack itself is also manipulated throughout the code after creating the error with e.g., Error.captureStackTrace. So manipulating error.toString() does not work (also for another reason: even if adding a specific toString() to each error it does not mean that this function would ever be called as we could likely use Error.prototype.toString.call(error) instead).
I guess the best way to solve this is to just adding the properties that we want to show directly on the error as enumerable properties. That way these properties would always be visible when inspecting them (no matter if in the repl or elsewhere).
The repl would probably need some extra love but that should be fine.
Here an example how it would look like when changing the name and printing code as well:
{ TypeError: The "fd" argument must be of type number. Received type undefined
at Object.fs.write (fs.js:630:3)
at repl:1:17
at Script.runInThisContext (vm.js:89:20) code: 'ERR_INVALID_ARG_TYPE' }
So manipulating error.toString() does not work (also for another reason: even if add a specific toString()
I don't understand why - wouldn't it result in the same output that is output right now in v10 with the modified name?
In any case, I think going through util.inspect() is better because it can print name and code in an easy to understand way (that these are properties I can introspect).
Maybe there could be some work on util.inspect() to not jam code on the last line of the stack trace to make it more visible.
@felixfbeckertoString() is never called when inspecting or logging (that uses inspect internally). That is the reason why it would not work. And if you add a individual toString() function and use the prototype toString function, it would still not be called. As in:
@Trott can't this be considered for Node 11? I would hate to have to live with this mistake for the rest of Node's life. Since name is currently a human-readable display property, changing it back to a programmatically-inspectable one would be a low-impact change (all code that was changed to not rely on name anymore would continue to work).
As I tried to point out above this is not an easy task to solve. I do not know of any solution that allows exactly what we want. That was also the reason why @jasnell originally implemented it the way it is.
After my recent changes all extra properties will be visible by default. That was not the case so far but it should already improve the situation.
I am not sure anymore if I already checked the following: when accessing the error stack (or calling toString if there is no stack) we could manipulate the output string to add the name. This would likely work in most cases. This would probably be fine since almost everyone would also either call console.log or util.inspect on it and that would result in what we want. However, if someone accesses the stack directly, that information will be lost. The same if someone uses another inspection module than util.inspect. I am somewhat on the edge about that and would like others to decide if this would be "sufficient" or not. Anything else would probably not work at all - at least I can not think of any other solution.
@jasnell there was no PR that changed the behavior.
And I am not sure how we can address this properly. The only working way for me was to set the name in the constructor, access the stack trace (so it generates the one with the code) and reset the name to the proper one without code. This works relatively well. However, in case the application exits due to the error the stack is generated again (seems like C++ code does that). Therefore the error code is not visible in those cases anymore. If someone knows how to prevent the stack trace from being generated again, we could try this workaround.
Why do we have to keep the error code in err.name in the first place? Can't we just put it in err.message (after the colon)? I doubt if anybody is relying on any of these two being exactly the same as before since the whole point of these errors is to rely on err.code instead, so I don't think we would break anybody if we just do a semver-major bump and change err.name as well as err.message?
Also, formatting err.name with Name [${err.code}] seems to break Web compatibility in our Web API implementations (at least it breaks WPT and has to be worked around with #22556).
The only significant difference is the addition of the code property which would just need to be documented as a Node.js specific extension. instanceof Error / instanceof TypeError still work for these as expected.
But WPT does not use instanceof because exceptions on the Web can come from different realms (so do Node.js exceptions thrown from different vms), and in addition to err.code, err.name is currently another significant difference from the simple exceptions.
If we want to move the code from err.name to err.message, should we do this before v11, or should we wait and do this in the v11 cycle (pre-v12)? (I prefer to do this before v11 is cut, assuming we manage to revamp our tests in time, but I'll defer to @jasnell )
(Also, we can use help from the upcoming code-and-learn to fix the Error [CODE] in tests, currently there are 200+ instances in 68 files)
(Also, we can use help from the upcoming code-and-learn to fix the Error [CODE] in tests, currently there are 200+ instances in 68 files)
Do we know what we'd want it replaced with? Checking err.code for sure, but also.... instanceof check on the original Error type (RangeError, TypeError, etc.)? Or something else?
@Trottcommon.expectsError({type: TypeError, code: 'ERR_CODE'}) as what we used to do. We can change common.expectsError to check err.name matches type.constructor.name instead of using instanceof later or now (that's what the WPT harness does in essence to work around errors in different realms)
@Trottcommon.expectsError({type: TypeError, code: 'ERR_CODE'}) as what we used to do.
I can live with that. Ideally, I would prefer to avoid the magic/special type property in common.expectsError() because it won't be available when we move (back) to assert.throws() (which is what I believe @BridgeAR and I have both been imagining we'd do after 8.x is end-of-life).
EDIT: Although I'm sure we can figure out some way to tell assert.throws() to check it. assert.throws(block, object, options)? This is getting off-topic for this issue. I'll have this conversation elsewhere... :-D
I'm strongly -1 on moving the code to err.message. There is far more code out in the wild that parses through the error message than pays attention to the error name and moving the code to the message carries a very high risk of breaking code, which would force us to treat changing error messages as semver-major again, which takes us in the wrong direction. Including the code in the name was not a snap decision, it was well thought out as being the least disruptive approach.
What I do think is a viable approach long term is to make a proposal to TC-39 to add code as a standard property along with a specification that ensures it's value is always serialized with the error message and stack.
I'm going to take this issue off the 11.0.0 milestone as it would require (a) having a PR, (b) having consensus on the approach, and (c) having TSC sign off to get pulled in to 11 at this point.
There is far more code out in the wild that parses through the error message than pays attention to the error name and moving the code to the message carries a very high risk of breaking code, which would force us to treat changing error messages as semver-major again
If we still cannot move err.code out of err.name because people parse err.message (in the stacktrace or just as err.message), what difference does it make to have a err.code or not? Aren't we still stuck at the semver-major situation if we cannot move err.code out of err.name simply because that will change err.message/the stacktrace?
It's hard to imagine that someone would rely on Error [ERR_CODE] being exactly the same format knowing that ERR_CODE is already available as err.code and is much more reliable (at least the format of error.name is not documented while error.code is in https://nodejs.org/api/errors.html). How would the code relying on this behavior look like?
@jasnell IIUC, you might be talking about places like
constmatch=err.message.match(someRe);// expects there is no `[ERR_CODE]` suddenly showing up at the start
? But I believe for errors like those we don't even touch the err.name anyway? (e.g. uvException, errnoException, .etc) They are just the most simple Error with "Error" as name right now, so the change does not effect them anyway. The errors that do have their names formatted as Error [ERR_CODE] (i.e. the errors formatted with the E() function in internal/errors.js ) are currently all free to change their messages without going semver-major, so adding [ERR_CODE] to their error message doesn't change our policy.
Activity
BridgeAR commentedon May 15, 2018
I just had a look at this and played around how this could be fixed and it turns out to be quite a difficult task at hand.
We use
util.inspectfor everything that we log and when inspecting errors, we return the error stack (plus enumerable properties on the error).Manipulating the stack is not an option because the stack itself is also manipulated throughout the code after creating the error with e.g.,
Error.captureStackTrace. So manipulatingerror.toString()does not work (also for another reason: even if adding a specifictoString()to each error it does not mean that this function would ever be called as we could likely useError.prototype.toString.call(error)instead).I guess the best way to solve this is to just adding the properties that we want to show directly on the error as enumerable properties. That way these properties would always be visible when inspecting them (no matter if in the repl or elsewhere).
BridgeAR commentedon May 15, 2018
The repl would probably need some extra love but that should be fine.
Here an example how it would look like when changing the name and printing
codeas well:felixfbecker commentedon May 15, 2018
I don't understand why - wouldn't it result in the same output that is output right now in v10 with the modified
name?In any case, I think going through
util.inspect()is better because it can printnameandcodein an easy to understand way (that these are properties I can introspect).Maybe there could be some work on
util.inspect()to not jamcodeon the last line of the stack trace to make it more visible.BridgeAR commentedon May 15, 2018
@felixfbecker
toString()is never called when inspecting or logging (that uses inspect internally). That is the reason why it would not work. And if you add a individualtoString()function and use the prototypetoStringfunction, it would still not be called. As in:BridgeAR commentedon May 15, 2018
👍 I am going to look into it.
util: wrap error in brackets without stack
util: remove erroneous whitespace
util: fix inspected stack indentation
util: improve error inspection
util: wrap error in brackets without stack
util: remove erroneous whitespace
util: fix inspected stack indentation
util: improve error inspection
Trott commentedon Jun 3, 2018
This ship seems to have sailed, unfortunately. Closing.
felixfbecker commentedon Jun 4, 2018
@Trott can't this be considered for Node 11? I would hate to have to live with this mistake for the rest of Node's life. Since
nameis currently a human-readable display property, changing it back to a programmatically-inspectable one would be a low-impact change (all code that was changed to not rely onnameanymore would continue to work).Trott commentedon Jun 5, 2018
This needs more attention, but there's no errors team. @BridgeAR @joyeecheung @jasnell
BridgeAR commentedon Jun 5, 2018
As I tried to point out above this is not an easy task to solve. I do not know of any solution that allows exactly what we want. That was also the reason why @jasnell originally implemented it the way it is.
After my recent changes all extra properties will be visible by default. That was not the case so far but it should already improve the situation.
I am not sure anymore if I already checked the following: when accessing the error stack (or calling toString if there is no stack) we could manipulate the output string to add the name. This would likely work in most cases. This would probably be fine since almost everyone would also either call
console.logorutil.inspecton it and that would result in what we want. However, if someone accesses the stack directly, that information will be lost. The same if someone uses another inspection module thanutil.inspect. I am somewhat on the edge about that and would like others to decide if this would be "sufficient" or not. Anything else would probably not work at all - at least I can not think of any other solution.repl: improve error output
repl: refactor ERR_SCRIPT_EXECUTION_INTERRUPTED stack handling
BridgeAR commentedon Aug 28, 2018
I might have found a solution. Trying it out right now.
BridgeAR commentedon Aug 29, 2018
I tried it out and my approach works relatively well in most cases but as I feared above not in all cases. Therefore I still see no other way.
repl: improve error output
repl: refactor ERR_SCRIPT_EXECUTION_INTERRUPTED stack handling
jasnell commentedon Oct 3, 2018
what's the current status on this? Will this be a semver major or no?
BridgeAR commentedon Oct 3, 2018
@jasnell there was no PR that changed the behavior.
And I am not sure how we can address this properly. The only working way for me was to set the name in the constructor, access the stack trace (so it generates the one with the code) and reset the name to the proper one without code. This works relatively well. However, in case the application exits due to the error the stack is generated again (seems like C++ code does that). Therefore the error code is not visible in those cases anymore. If someone knows how to prevent the stack trace from being generated again, we could try this workaround.
joyeecheung commentedon Oct 3, 2018
Why do we have to keep the error code in
err.namein the first place? Can't we just put it inerr.message(after the colon)? I doubt if anybody is relying on any of these two being exactly the same as before since the whole point of these errors is to rely onerr.codeinstead, so I don't think we would break anybody if we just do a semver-major bump and changeerr.nameas well aserr.message?joyeecheung commentedon Oct 3, 2018
Also, formatting
err.namewithName [${err.code}]seems to break Web compatibility in our Web API implementations (at least it breaks WPT and has to be worked around with #22556).This was brought up in #11299 @jasnell
But WPT does not use
instanceofbecause exceptions on the Web can come from different realms (so do Node.js exceptions thrown from different vms), and in addition toerr.code,err.nameis currently another significant difference from the simple exceptions.(So..maybe #11299 should be reopened?)
BridgeAR commentedon Oct 3, 2018
I would also prefer to have a "clean" name property instead of adding the code to it but it's not a strong opinion.
joyeecheung commentedon Oct 3, 2018
If we want to move the code from
err.nametoerr.message, should we do this before v11, or should we wait and do this in the v11 cycle (pre-v12)? (I prefer to do this before v11 is cut, assuming we manage to revamp our tests in time, but I'll defer to @jasnell )(Also, we can use help from the upcoming code-and-learn to fix the
Error [CODE]in tests, currently there are 200+ instances in 68 files)Trott commentedon Oct 3, 2018
Do we know what we'd want it replaced with? Checking
err.codefor sure, but also....instanceofcheck on the original Error type (RangeError,TypeError, etc.)? Or something else?joyeecheung commentedon Oct 3, 2018
@Trott
common.expectsError({type: TypeError, code: 'ERR_CODE'})as what we used to do. We can changecommon.expectsErrorto checkerr.namematchestype.constructor.nameinstead of usinginstanceoflater or now (that's what the WPT harness does in essence to work around errors in different realms)Trott commentedon Oct 3, 2018
I can live with that. Ideally, I would prefer to avoid the magic/special
typeproperty incommon.expectsError()because it won't be available when we move (back) toassert.throws()(which is what I believe @BridgeAR and I have both been imagining we'd do after 8.x is end-of-life).EDIT: Although I'm sure we can figure out some way to tell
assert.throws()to check it.assert.throws(block, object, options)? This is getting off-topic for this issue. I'll have this conversation elsewhere... :-Djasnell commentedon Oct 3, 2018
I'm strongly -1 on moving the code to
err.message. There is far more code out in the wild that parses through the error message than pays attention to the error name and moving the code to the message carries a very high risk of breaking code, which would force us to treat changing error messages as semver-major again, which takes us in the wrong direction. Including the code in the name was not a snap decision, it was well thought out as being the least disruptive approach.What I do think is a viable approach long term is to make a proposal to TC-39 to add
codeas a standard property along with a specification that ensures it's value is always serialized with the error message and stack.jasnell commentedon Oct 3, 2018
I'm going to take this issue off the 11.0.0 milestone as it would require (a) having a PR, (b) having consensus on the approach, and (c) having TSC sign off to get pulled in to 11 at this point.
joyeecheung commentedon Oct 3, 2018
If we still cannot move
err.codeout oferr.namebecause people parseerr.message(in the stacktrace or just aserr.message), what difference does it make to have aerr.codeor not? Aren't we still stuck at the semver-major situation if we cannot moveerr.codeout oferr.namesimply because that will changeerr.message/the stacktrace?It's hard to imagine that someone would rely on
Error [ERR_CODE]being exactly the same format knowing thatERR_CODEis already available aserr.codeand is much more reliable (at least the format oferror.nameis not documented whileerror.codeis in https://nodejs.org/api/errors.html). How would the code relying on this behavior look like?instead of
?
joyeecheung commentedon Oct 3, 2018
@jasnell IIUC, you might be talking about places like
? But I believe for errors like those we don't even touch the
err.nameanyway? (e.g.uvException,errnoException, .etc) They are just the most simpleErrorwith"Error"as name right now, so the change does not effect them anyway. The errors that do have their names formatted asError [ERR_CODE](i.e. the errors formatted with theE()function in internal/errors.js ) are currently all free to change their messages without going semver-major, so adding[ERR_CODE]to their error message doesn't change our policy.errors: update error name
lib: refactor Error.captureStackTrace() usage
n-api: remove code from error name