Skip to content
This repository has been archived by the owner on Apr 22, 2023. It is now read-only.

doc: add documentation for errors #8114

Closed

Conversation

chrisdickinson
Copy link

This adds documentation around what errors are generated by Node and how to intercept them.

The rendered docs are available here.

@chrisdickinson
Copy link
Author

Ideally I'd like to backport this to v0.10 as well -- should I open another PR for that?

@chrisdickinson
Copy link
Author

This partially addresses #8103.

ReferenceError instances will have an `.arguments` member that is an array containing
one element -- a string representing the variable that was not defined.

try {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason not to enable syntax highlighting?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I'm following the style of existing docs. I'm not sure that the build setup for the docs enables github flavored markdown. Maybe something to look into in a subsequent PR?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought they did, Streams has syntax highlighting at an rate

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah! Good catch. I was looking at the http docs. I'll add that!

@nvcexploder
Copy link

this looks rad @chrisdickinson!

#### error.stack

A property that, when **accessed**, returns a string representing the point in the program
at which this error was instantiated. An example stacktrace follows:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and line 51 one read a bit oddly

A general error object -- its imparts no meaning to the situation that
generated the error. Errors capture a "stack trace" detailing the point
in the program at which they were instantiated, and may provide an
description of the error.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/an description/a description

@chrisdickinson
Copy link
Author

@tjfontaine - could I ask you to do a quick final review?


<!--type=class-->

A general error object -- its imparts no meaning to the situation that

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo "its imparts"

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good eye! Thanks!

@indutny
Copy link
Member

indutny commented Aug 27, 2014

Needs an eye of some native-english core contributor: @trevnorris or @tjfontaine


<!--type=misc-->

Errors generated by Node.JS fall into two categories: JavaScript errors and system

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"System errors" doesn't make a lot of sense, as I would clasify these as unrecoverable exceptions. For example, JS FATAL ALLOCATION. Nothing you can do about that, and it's not an instanceof Error.

There are also a set of recoverable and unrecoverable JS errors that are emitted from V8. For example:

process.on('uncaughtException', function(er) {
  console.log(er);
});

var b = f;

// output: [ReferenceError: f is not defined]

So V8 allows the ReferenceError to be caught. But in the following:

process.on('uncaughtException', function(er) {
  console.log(er);
});

var b = f f;

There is no ability to recover, the following is printed and the application exits.

/tmp/test-exception.js:5
var b = f f;
          ^
SyntaxError: Unexpected identifier
    at exports.runInThisContext (vm.js:69:16)
    at Module._compile (module.js:443:25)
    at Object.Module._extensions..js (module.js:478:10)
    at Module.load (module.js:355:32)
    at Function.Module._load (module.js:310:12)
    at Function.Module.runMain (module.js:501:10)
    at startup (node.js:124:16)
    at node.js:811:3

So the SyntaxError is still an instanceof Error, but it's unrecoverable.

Bringing this back around. There are JS Error objects, and there are recoverable and unrecoverable exceptions.

All recoverable exceptions from Node.js and V8 will be an instanceof Error, but not all unrecoverable exceptions. This does not imply that all recoverable exceptions are instanceof Error, since anything can be thrown in JS. For example throw 42; will propagate the number 42 to be caught, and it's not instanceof Error.

That being said, the only reason that all recoverable exceptions thrown from Node.js and V8 are instanceof Error is by convention. There's nothing forcing Node to only throw Error objects.

This make sense?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addendum: Only V8 is allowed to throw unrecoverable exceptions that are also instanceof Error.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think what you might have been going for with "system error" is like when fs.stat('not/here', cb) passes the system error of ENOENT. That entire mechanism has been setup by Node and is not fundamentally tied to the language or the idea of an exception.

Instead it should probably be explained separately that Node will wrap these types of "system errors" in a js error object so it can be inspected by the user. But it's dependent on the user having made a proper API call.

@dshaw
Copy link

dshaw commented Sep 2, 2014

@chrisdickinson This is coming together quite nicely. 👏👏👏

message. Its `.stack` will represent the point in the program at which `new Error`
was called. Stack traces are subject to [V8's stack trace API](https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi).
Stack traces only extend as far back as the current event loop tick, *or* a number of frames given by
`Error.stackTraceLimit`, whichever is smaller.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as far back as the current event loop tick

"to the beginning of synchronous code execution"


The location information will be one of:

* `native`, if the frame represents a call internal to V8.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, specify that "native" here means js function built into V8. It doesn't include transversal through C++ "native" functions.

@trevnorris
Copy link

Wanted to address some general guidelines writing documentation:

  1. No personal pronouns. There is no need to address the reading audience, or some third party developer using the API. Instead simply state what the API does.
  2. Drop the usage of dash. All the cases where I saw it used was when there existed additional clarifying information. This additional information should be placed in parentheses inside the sentence of which it clarifies.
  3. Fewer usages of pronouns (e.g. "it"). It is better to be more explicit and have redundancy using the noun.

As far as helping users understand the nature of "errors". They are broken up into the following:

  1. Built-in JS errors (i.e. errors that have been specified by the ECMAScript standard) are simply constructors that return a new object instances.
    1. JS errors have no direct relation to exceptions.
  2. There are recoverable and unrecoverable exceptions.
    1. An exception does not have to be a JS error.
    2. V8 and Node only throw JS errors by convention. Not because it is required.
    3. Only V8 can throw an unrecoverable JS error instance (e.g. SyntaxError).
      1. These exceptions are unrecoverable within the JS context they are run. Meaning a wrapping context (e.g. usage of the vm module) can still catch these exceptions.
      2. Process level unrecoverable exceptions are not JS errors. These come generally in the form of a low-level call to assert() or abort().
  3. Node wraps "system errors" in a built-in JS error instance.
    1. "System errors" have no direct relation to built-in JS errors or exceptions.
    2. "System errors" are generated after a successful API call and denote an error occurred on the underlying system.

Thanks for all your work on this. It will be nice to have a formative document explaining these specifics.

@chrisdickinson
Copy link
Author

@dshaw Thanks!

@trevnorris Thanks for your review.

I initially shied away from explaining the distinction between exceptions and errors: it's hard to balance between what is Node's responsibility to explain vs. what should be covered by more JS specific material (like MDN, the spec, YDKJS, et al). Ultimately, though, since the document touches on error propagation, it has to at least build a common vocabulary regarding exceptions, errors, propagation strategies, and system- vs. language-level errors. Some structural rework is in order -- I'll work on another draft incorporating your comments.

Apologies for the copious number of emdashes -- it's a particular weakness of my writing. :)

Re: Unrecoverable errors: is there an underlying concept of unrecoverable errors in v8, or is this a higher-level concept?

@trevnorris
Copy link

@chrisdickinson Thanks for putting up with all my comments. :)

Re: Unrecoverable errors: is there an underlying concept of unrecoverable errors in v8, or is this a higher-level concept?

First let's specify, we're addressing unrecoverable exceptions (note: there's no such thing as an unrecoverable error) that are thrown as part of the V8 JS VM. Some exceptions thrown by V8 are recoverable. Some aren't. (This was discussed in #8114 (comment)). It simply means that the exception isn't caught by the V8 exception handling API that's exposed.

Basically it's forcing the running JS context to be brought down. As you showed in your vm example, it's possible to not bring down the process if an unrecoverable JS exception only propagates as far as a contained context.

I'd like to revise my complicated list on how these are broken down:

  1. JS Error Objects: New objects instantiated from a built-in JS Error function (built-in, meaning specified by the ECMAScript standard)
    1. Error
    2. TypeError
    3. RangeError
    4. etc. etc....
  2. Exceptions: The two types of exceptions that exist are from the JS VM and the system.
    1. JS exceptions only propagate as far as the v8::Context in which they live. Since, in Node, the main runtime context is tied to the main thread. So if the main context reaches an un-handled exception then the process will also be brought down.
      1. Unrecoverable JS Exception: means there is no way to catch and handle the exception from the v8::Context in which the code was run. An example would be a thrown SyntaxError from V8. (Only V8 is allowed to throw unrecoverable JS exceptions, AFAIK).
      2. Recoverable JS Exception: means the user is allowed to catch and handle the exception. Most exceptions fall into this category.
    2. System exceptions cannot be caught in JS, as they are not any type of JS value, and will always bring down the process. These are usually a failed assert() or an abort().
  3. System Errors: Errors that occur to the underlying system when making API calls. For example, trying to fs.stat() a file that doesn't exist. Node wraps these system errors in a JS error object instance so information about what happened can be propagated to the user.

I feel that is a better delineation of the set of pertinent topics, and while I agree there is plenty of material that properly explains the difference between errors and exceptions, there should at least be a brief mention in the documentation. Probably followed by links to more in-depth materials.

Thanks again for all your hard work on this. :)

@jasnell
Copy link
Member

jasnell commented Aug 14, 2015

@chrisdickinson ... what do you want to do with this one?

@cjihrig
Copy link

cjihrig commented Aug 15, 2015

This landed in io.js/converged node right? If so, I think we should just close this.

@jasnell
Copy link
Member

jasnell commented Aug 15, 2015

I'm good with that! @chrisdickinson , closing this but can reopen if there's something we missed.

@jasnell jasnell closed this Aug 15, 2015
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.