diff --git a/doc/api/_toc.markdown b/doc/api/_toc.markdown index cf643e28cf98eb..c838ab383e2ed7 100644 --- a/doc/api/_toc.markdown +++ b/doc/api/_toc.markdown @@ -12,6 +12,7 @@ * [Debugger](debugger.html) * [DNS](dns.html) * [Domain](domain.html) +* [Errors](errors.html) * [Events](events.html) * [File System](fs.html) * [Globals](globals.html) diff --git a/doc/api/all.markdown b/doc/api/all.markdown index 0f0ec6338d8a50..c01752feed7cbf 100644 --- a/doc/api/all.markdown +++ b/doc/api/all.markdown @@ -11,6 +11,7 @@ @include dgram @include dns @include domain +@include errors @include events @include fs @include globals diff --git a/doc/api/errors.markdown b/doc/api/errors.markdown new file mode 100644 index 00000000000000..92cae6d256d616 --- /dev/null +++ b/doc/api/errors.markdown @@ -0,0 +1,470 @@ +# Errors + + + +Errors generated by io.js fall into two categories: JavaScript errors and system +errors. All errors inherit from or are instances of JavaScript's [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) +class and are guaranteed to provide *at least* the attributes available on that +class. + +When an operation is not permitted due to language-syntax or +language-runtime-level reasons, a **JavaScript error** is generated and thrown +as an **exception**. If an operation is not allowed due to system-level +restrictions, a **system error** is generated. Client code is then given the +opportunity to **intercept** this error based on how the API **propagates** it. + +The style of API called determines how generated errors are handed back, or +**propagated**, to client code, which in turn informs how the client may **intercept** +the error. Exceptions can be intercepted using the `try / catch` construct; +other propagation strategies are covered [below](#errors_error_propagation_and_interception). + +## JavaScript Errors + + + +JavaScript errors typically denote that an API is being used incorrectly, or that +there is a problem with the program as written. + +### Class: Error + + + +A general error object. Unlike other error objects, `Error` instances do not +denote any specific circumstance of why the error occurred. Errors capture a +"stack trace" detailing the point in the program at which they were +instantiated, and may provide a description of the error. + +**Note**: io.js will generate this class of error to encapsulate system +errors as well as plain JavaScript errors. + +#### new Error(message) + +Instantiates a new Error object and sets its `.message` property to the provided +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/wiki/JavaScriptStackTraceApi). +Stack traces only extend to the beginning of synchronous code execution, *or* a number of frames given by +`Error.stackTraceLimit`, whichever is smaller. + +#### error.message + +A string of the value passed to `Error()` upon instantiation. The message will +also appear in the first line of the stack trace of the error. Changing this +property *may not* change the first line of the stack trace. + +#### 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: + + Error: Things keep happening! + at /home/gbusey/file.js:525:2 + at Frobnicator.refrobulate (/home/gbusey/business-logic.js:424:21) + at Actor. (/home/gbusey/actors.js:400:8) + at increaseSynergy (/home/gbusey/actors.js:701:6) + +The first line is formatted as `: `, and it is followed +by a series of stack frames (each line beginning with "at "). Each frame describes +a call site in the program that lead to the error being generated. V8 attempts to +display a name for each function (by variable name, function name, or object +method name), but occasionally it will not be able to find a suitable name. If +V8 cannot determine a name for the function, only location information will be +displayed for that frame. Otherwise, the determined function name will be displayed +with location information appended in parentheses. + +Frames are **only** generated for JavaScript functions. If, for example, execution +synchronously passes through a C++ addon function called `cheetahify`, which itself +calls a JavaScript function, the frame representing the `cheetahify` call will **not** +be present in stacktraces: + +```javascript +var cheetahify = require('./native-binding.node'); + +function makeFaster() { + // cheetahify *synchronously* calls speedy. + cheetahify(function speedy() { + throw new Error('oh no!'); + }); +} + +makeFaster(); // will throw: +// /home/gbusey/file.js:6 +// throw new Error('oh no!'); +// ^ +// Error: oh no! +// at speedy (/home/gbusey/file.js:6:11) +// at makeFaster (/home/gbusey/file.js:5:3) +// at Object. (/home/gbusey/file.js:10:1) +// at Module._compile (module.js:456:26) +// at Object.Module._extensions..js (module.js:474:10) +// at Module.load (module.js:356:32) +// at Function.Module._load (module.js:312:12) +// at Function.Module.runMain (module.js:497:10) +// at startup (node.js:119:16) +// at node.js:906:3 +``` + +The location information will be one of: + +* `native`, if the frame represents a call internal to V8 (as in `[].forEach`). +* `plain-filename.js:line:column`, if the frame represents a call internal to io.js. +* `/absolute/path/to/file.js:line:column`, if the frame represents a call in a user program, or its dependencies. + +It is important to note that the string representing the stacktrace is only +generated on **access**: it is lazily generated. + +The number of frames captured by the stack trace is bounded by the smaller of +`Error.stackTraceLimit` or the number of available frames on the current event +loop tick. + +System-level errors are generated as augmented Error instances, which are detailed +[below](#errors_system_errors). + +#### Error.captureStackTrace(targetObject[, constructorOpt]) + +Creates a `.stack` property on `targetObject`, which when accessed returns +a string representing the location in the program at which `Error.captureStackTrace` +was called. + +```javascript +var myObject = {}; + +Error.captureStackTrace(myObject); + +myObject.stack // similar to `new Error().stack` +``` + +The first line of the trace, instead of being prefixed with `ErrorType: +message`, will be the result of `targetObject.toString()`. + +`constructorOpt` optionally accepts a function. If given, all frames above +`constructorOpt`, including `constructorOpt`, will be omitted from the generated +stack trace. + +This is useful for hiding implementation details of error generation from the +end user. A common way of using this parameter is to pass the current Error +constructor to it: + +```javascript + +function MyError() { + Error.captureStackTrace(this, MyError); +} + +// without passing MyError to captureStackTrace, the MyError +// frame would should up in the .stack property. by passing +// the constructor, we omit that frame and all frames above it. +new MyError().stack + +``` + +#### Error.stackTraceLimit + +Property that determines the number of stack frames collected by a stack trace +(whether generated by `new Error().stack` or `Error.captureStackTrace(obj)`). + +The initial value is `10`. It may be set to any valid JavaScript number, which +will effect any stack trace captured *after* the value has been changed. If set +to a non-number value, stack traces will not capture any frames and will report +`undefined` on access. + +### Class: RangeError + +A subclass of Error that indicates that a provided argument was not within the +set or range of acceptable values for a function; whether that be a numeric +range, or outside the set of options for a given function parameter. An example: + +```javascript +require('net').connect(-1); // throws RangeError, port should be > 0 && < 65536 +``` + +io.js will generate and throw RangeError instances *immediately* -- they are a form +of argument validation. + +### Class: TypeError + +A subclass of Error that indicates that a provided argument is not an allowable +type. For example, passing a function to a parameter which expects a string would +be considered a TypeError. + +```javascript +require('url').parse(function() { }); // throws TypeError, since it expected a string +``` + +io.js will generate and throw TypeError instances *immediately* -- they are a form +of argument validation. + +### Class: ReferenceError + +A subclass of Error that indicates that an attempt is being made to access a variable +that is not defined. Most commonly it indicates a typo, or an otherwise broken program. +While client code may generate and propagate these errors, in practice only V8 will do +so. + +```javascript +doesNotExist; // throws ReferenceError, doesNotExist is not a variable in this program. +``` + +ReferenceError instances will have an `.arguments` member that is an array containing +one element -- a string representing the variable that was not defined. + +```javascript +try { + doesNotExist; +} catch(err) { + err.arguments[0] === 'doesNotExist'; +} +``` + +Unless the userland program is dynamically generating and running code, +ReferenceErrors should always be considered a bug in the program, or its +dependencies. + +### Class: SyntaxError + +A subclass of Error that indicates that a program is not valid JavaScript. +These errors may only be generated and propagated as a result of code +evaluation. Code evaluation may happen as a result of `eval`, `Function`, +`require`, or [vm](vm.html). These errors are almost always indicative of a broken +program. + +```javascript +try { + require("vm").runInThisContext("binary ! isNotOk"); +} catch(err) { + // err will be a SyntaxError +} +``` + +SyntaxErrors are unrecoverable from the context that created them – they may only be caught +by other contexts. + +### Exceptions vs. Errors + + + +A JavaScript "exception" is a value that is thrown as a result of an invalid operation or +as the target of a `throw` statement. While it is not required that these values inherit from +`Error`, all exceptions thrown by io.js or the JavaScript runtime *will* be instances of Error. + +Some exceptions are *unrecoverable* at the JavaScript layer. These exceptions will always bring +down the process. These are usually failed `assert()` checks or `abort()` calls in the C++ layer. + +## System Errors + +System errors are generated in response to a program's runtime environment. +Ideally, they represent operational errors that the program needs to be able to +react to. They are generated at the syscall level: an exhaustive list of error +codes and their meanings is available by running `man 2 intro` on most Unices; +or [online](http://man7.org/linux/man-pages/man2/intro.2.html). + +In io.js, system errors are represented as augmented Error objects -- not full +subclasses, but instead an error instance with added members. + +### Class: System Error + +#### error.syscall + +A string representing the [syscall](http://man7.org/linux/man-pages/man2/syscall.2.html) that failed. + +#### error.errno +#### error.code + +A string representing the error code, which is always `E` followed by capital +letters, and may be referenced in `man 2 intro`. + +### Common System Errors + +This list is **not exhaustive**, but enumerates many of the common system errors when +writing a io.js program. An exhaustive list may be found [here](http://man7.org/linux/man-pages/man2/intro.2.html). + +#### EPERM: Operation not permitted + +An attempt was made to perform an operation that requires appropriate +privileges. + +#### ENOENT: No such file or directory + +Commonly raised by [fs](fs.html) operations; a component of the specified pathname +does not exist -- no entity (file or directory) could be found by the given path. + +#### EACCES: Permission denied + +An attempt was made to access a file in a way forbidden by its file access +permissions. + +#### EEXIST: File exists + +An existing file was the target of an operation that required that the target +not exist. + +#### ENOTDIR: Not a directory + +A component of the given pathname existed, but was not a directory as expected. +Commonly raised by [fs.readdir](fs.html#fs_fs_readdir_path_callback). + +#### EISDIR: Is a directory + +An operation expected a file, but the given pathname was a directory. + +#### EMFILE: Too many open files in system + +Maxiumum number of [file descriptors](http://en.wikipedia.org/wiki/File_descriptor) allowable on the system has +been reached, and requests for another descriptor cannot be fulfilled until +at least one has been closed. + +Commonly encountered when opening many files at once in parallel, especially +on systems (in particular, OS X) where there is a low file descriptor limit +for processes. To remedy a low limit, run `ulimit -n 2048` in the same shell +that will run the io.js process. + +#### EPIPE: Broken pipe + +A write on a pipe, socket, or FIFO for which there is no process to read the +data. Commonly encountered at the [net](net.html) and [http](http.html) layers, indicative that +the remote side of the stream being written to has been closed. + +#### EADDRINUSE: Address already in use + +An attempt to bind a server ([net](net.html), [http](http.html), or [https](https.html)) to a local +address failed due to another server on the local system already occupying +that address. + +#### ECONNRESET: Connection reset by peer + +A connection was forcibly closed by a peer. This normally results +from a loss of the connection on the remote socket due to a timeout +or reboot. Commonly encountered via the [http](http.html) and [net](net.html) modules. + +#### ECONNREFUSED: Connection refused + +No connection could be made because the target machine actively refused +it. This usually results from trying to connect to a service that is inactive +on the foreign host. + +#### ENOTEMPTY: Directory not empty + +A directory with entries was the target of an operation that requires +an empty directory -- usually [fs.unlink](fs.html#fs_fs_unlink_path_callback). + +#### ETIMEDOUT: Operation timed out + +A connect or send request failed because the connected party did not properly +respond after a period of time. Usually encountered by [http](http.html) or [net](net.html) -- +often a sign that a connected socket was not `.end()`'d appropriately. + +## Error Propagation and Interception + + + +All io.js APIs will treat invalid arguments as exceptional -- that is, if passed +invalid arguments, they will *immediately* generate and throw the error as an +exception, even if they are an otherwise asynchronous API. + +Synchronous APIs (like +[fs.readFileSync](fs.html#fs_fs_readfilesync_filename_options)) will throw the +error. The act of *throwing* a value (in this case, the error) turns the value +into an **exception**. Exceptions may be caught using the `try { } catch(err) +{ }` construct. + +Asynchronous APIs have **two** mechanisms for error propagation; one mechanism +for APIs that represent a single operation, and one for APIs that represent +multiple operations over time. + +### Node style callbacks + + + +Single operation APIs take "node style callbacks" -- a +function provided to the API as an argument. The node style callback takes +at least **one** argument -- `error` -- that will either be `null` (if no error +was encountered) or an `Error` instance. For instance: + +```javascript +var fs = require('fs'); + +fs.readFile('/some/file/that/does-not-exist', function nodeStyleCallback(err, data) { + console.log(err) // Error: ENOENT + console.log(data) // undefined / null +}); + +fs.readFile('/some/file/that/does-exist', function(err, data) { + console.log(err) // null + console.log(data) // +}) +``` + +Note that `try { } catch(err) { }` **cannot** intercept errors generated by +asynchronous APIs. A common mistake for beginners is to try to use `throw` +inside their node style callback: + +```javascript +// THIS WILL NOT WORK: +var fs = require('fs'); + +try { + fs.readFile('/some/file/that/does-not-exist', function(err, data) { + // mistaken assumption: throwing here... + if (err) { + throw err; + } + }); +} catch(err) { + // ... will be caught here -- this is incorrect! + console.log(err); // Error: ENOENT +} +``` + +This will not work! By the time the node style callback has been called, the +surrounding code (including the `try { } catch(err) { }` will have already +exited. Throwing an error inside a node style callback **will crash the process** in most cases. +If [domains](domain.html) are enabled, they may intercept the thrown error; similarly, if a +handler has been added to `process.on('uncaughtException')`, it will intercept +the error. + +### Error events + + + +The other mechanism for providing errors is the "error" event. This is +typically used by [stream-based](stream.html) and [event emitter-based](events.html#events_class_events_eventemitter) APIs, which +themselves represent a series of asynchronous operations over time (versus a +single operation that may pass or fail). If no "error" event handler is +attached to the source of the error, the error will be thrown. At this point, +it will crash the process as an unhandled exception unless [domains](domain.html) are +employed appropriately or [process.on('uncaughtException')](process.html#process_event_uncaughtexception) has a handler. + +```javascript +var net = require('net'); + +var connection = net.connect('localhost'); + +// adding an "error" event handler to a stream: +connection.on('error', function(err) { + // if the connection is reset by the server, or if it can't + // connect at all, or on any sort of error encountered by + // the connection, the error will be sent here. + console.error(err); +}); + +connection.pipe(process.stdout); +``` + +The "throw when no error handlers are attached behavior" is not limited to APIs +provided by io.js -- even user created event emitters and streams will throw +errors when no error handlers are attached. An example: + +```javascript +var events = require('events'); + +var ee = new events.EventEmitter; + +setImmediate(function() { + // this will crash the process because no "error" event + // handler has been added. + ee.emit('error', new Error('This will crash')); +}); +``` + +As with node style callbacks, errors generated this way *cannot* be intercepted +by `try { } catch(err) { }` -- they happen *after* the calling code has already +exited.