mirror of https://github.com/lukechilds/node.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
471 lines
17 KiB
471 lines
17 KiB
10 years ago
|
# Errors
|
||
|
|
||
|
<!--type=misc-->
|
||
|
|
||
|
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
|
||
|
|
||
|
<!--type=misc-->
|
||
|
|
||
|
JavaScript errors typically denote that an API is being used incorrectly, or that
|
||
|
there is a problem with the program as written.
|
||
|
|
||
|
### Class: Error
|
||
|
|
||
|
<!--type=class-->
|
||
|
|
||
|
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.<anonymous> (/home/gbusey/actors.js:400:8)
|
||
|
at increaseSynergy (/home/gbusey/actors.js:701:6)
|
||
|
|
||
|
The first line is formatted as `<error class name>: <error message>`, 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.<anonymous> (/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
|
||
|
|
||
|
<!--type=misc-->
|
||
|
|
||
|
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
|
||
|
|
||
|
<!--type=misc-->
|
||
|
|
||
|
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
|
||
|
|
||
|
<!--type=misc-->
|
||
|
|
||
|
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) // <Buffer: ba dd ca fe>
|
||
|
})
|
||
|
```
|
||
|
|
||
|
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
|
||
|
|
||
|
<!--type=misc-->
|
||
|
|
||
|
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.
|