Error objects

Property summary

Ecmascript Error objects have very few standard properties, so many Ecmascript implementations have added quite a few custom properties. Duktape uses standard Error properties but also borrows the most useful properties used by other implementations. The number of "own" properties of error objects is minimized to keep error objects as small as possible.

Error objects have the following properties (mostly inherited):

Property nameCompatibilityDescription
namestandardName of error, e.g. TypeError, inherited
messagestandardOptional message of error, own property, empty message inherited if absent
fileNameRhinoFilename related to error source, inherited accessor
lineNumberRhinoLinenumber related to error source, inherited accessor
stackV8Traceback as a multi-line human redable string, inherited accessor

If Duktape is compiled with traceback support:

If Duktape is compiled without traceback support:

When error objects are created using the Duktape API from C code and the caller does not give a format string for a message, the message property is set to a numeric error code given in the API call. The type of message will be number in this case; normally error messages are strings. In minimized Duktape builds all errors generated internally by Duktape use numeric error codes only.

An object is considered an "error object" if its internal prototype chain contains the (original) Error.prototype object. Only objects matching this criteria get augmented with e.g. traceback data.

Traceback

The stack property is an accessor (setter/getter) property which provides a printable traceback related to an error. The traceback reflects the call stack when the error object was created (not thrown). Traceback data is automatically collected and added to an object:

The data used to create the traceback is stored in an internal property (\xFFtracedata), in an internal and version-dependent format described error-objects.rst. You shouldn't access the traceback data directly.

The printable traceback format is intended to be human readable only. You shouldn't rely on an exact traceback format as it may change between versions. As an example of the current traceback format, the program:

// shortened from ecmascript-testcases/test-dev-traceback-example.js
try {
    decodeURIComponent('%e1%a9%01');  // invalid utf-8
} catch (e) {
    print(e.stack);
}

would print something like:

URIError: invalid input
        duk_bi_global.c:316
        decodeURIComponent  native strict preventsyield
        global ecmascript-testcases/test-dev-traceback-example.js:3 preventsyield

In builds where tracebacks are disabled, the stack accessor will return the same value as calling toString() on the error would. This means you can always print e.stack and get a useful output.

The most portable traceback printing approach is something like:

try {
    decodeURIComponent('%e1%a9%01');  // invalid utf-8
} catch (e) {
    // Print stacktrace on at least Duktape and V8, or a standard error
    // string otherwise.
    print(e.stack || e);
}

Attempt to write to stack is silently ignored. You can still override the accessor by defining an own property of the same name explicitly with Object.defineProperty(). This behavior differs from V8 where stack is an own property of the Error instance, and if you assign a value to stack, the value reads back as assigned.

Error handlers (errCreate and errThrow)

If Duktape.errCreate has been set, it is called right after Duktape has added traceback information to an object, and can process the error further or even replace the error value entirely. The error handler only gets called with Error instances, and its return value is used as the final error value. If the error handler throws an error, that error replaces the original error. The error handler is usually called only once per error. However, in corner cases related to constructors, the error handler can be called multiple times for a single error value.

An error handler should avoid overwriting any properties already present in an object, as that would be quite confusing for other code. In general, an error handler should always avoid throwing an error, as that error replaces the original error and would also be confusing. As a specific example, an error handler must not try to add a new property to a non-extensible object, as that would cause a TypeError.

Below is an example error handler for adding a creation timestamp to errors at their creation:

Duktape.errCreate = function (e) {
    if (!(e instanceof Error)) {
        // this check is not really needed because errCreate only gets
        // called with Error instances
        return e;
    }
    if ('created' in e) {
        // already augmented or conflicting property present
        return e;
    }
    if (!Object.isExtensible(e)) {
        // object not extensible, don't try to add a new property
        return e;
    }
    e.created = new Date();
    return e;
}

To remove the handler, delete the property (setting it to e.g. null does not work and causes a TypeError when Duktape attempts to call the null value):

// Remove error handler for error creation
delete Duktape.errCreate;

Similarly, if Duktape.errThrow has been set, it is called right before an error is thrown, and can process or replace the error value. Because Ecmascript allows any value type to be thrown, the error handler may get called with arbitrary input values (not just Error instances). It may also be called more than once for the same value because an error can be re-thrown multiple times.

For example, to add a throw timestamp (recording the first time the object has been thrown) to errors:

Duktape.errCreate = function (e) {
    if (!(e instanceof Error)) {
        // refuse to touch anything but Error instances
        return e;
    }
    if ('thrown' in e) {
        // already augmented or conflicting property present
        return e;
    }
    if (!Object.isExtensible(e)) {
        // object not extensible, don't try to add a new property
        return e;
    }
    e.thrown = new Date();
    return e;
}

Again, to remove the handler, delete the property:

// Remove error handler for error throwing
delete Duktape.errThrow;

Current limitations