Photo by PJH on Unsplash

On this page

This chapter follows Destructuring and spread.

Runtime errors versus static checks

TypeScript and other transpilers report compile-time issues before JavaScript runs. Plain JavaScript is parsed and executed just in time: invalid syntax throws SyntaxError at parse time; bad operations at runtime throw typed Error subclasses. There is no separate “checked exception” mechanism— callers choose whether to catch or let errors propagate to the host.

Common built-in types:

Type Typical trigger
SyntaxError Invalid source (if {)
ReferenceError Use of an undeclared binding
TypeError Wrong type for the operation (e.g. undefined() )
RangeError Numeric or repeat count out of range
URIError Bad encodeURI / decodeURI usage
AggregateError Several failures bundled (e.g. Promise.any)

try, catch, and finally

try runs a block. catch receives the thrown value (usually an Error instance). finally runs on both success and failure—use it for cleanup that must run regardless of return or throw.

try-catch.js
try {
risky();
} catch (err) {
console.error(`${err.name}: ${err.message}`);
} finally {
cleanup();
}

Creating an Error without throw does not stop execution—only throw begins unwinding.

throw.js
try {
throw new Error("Whoops!");
} catch (e) {
console.error(e.message);
}

The Error object and cause

Error instances expose message, name, and (in most engines) stack. ES2022 added cause in the options object to chain errors when rethrowing or wrapping.

cause.js
try {
parsePayload(raw);
} catch (err) {
throw new Error("Invalid payload", { cause: err });
}
try {
load();
} catch (wrapped) {
console.log(wrapped.cause); // original parse error
}

cause is intended for logging and inspection; console output may not expand it automatically.

Throwing your own errors

Use throw new TypeError(...) (or another built-in type) when the failure matches a standard category. Include a clear message; attach cause when translating low-level failures.

throw-type.js
if (Number.isNaN(month)) {
throw new TypeError("Month should be a number", { cause: { month } });
}

Custom Error subclasses

Extend Error for domain-specific failures. Set this.name, call super(message, options), and on V8 use Error.captureStackTrace(this, Constructor) so stacks point at your throw site.

custom-error.js
class ValidationError extends Error {
constructor(field, message, options) {
super(message, options);
this.name = "ValidationError";
this.field = field;
if (Error.captureStackTrace) {
Error.captureStackTrace(this, ValidationError);
}
}
}
try {
throw new ValidationError("email", "Invalid format");
} catch (e) {
if (e instanceof ValidationError) {
console.error(e.field, e.message);
} else {
throw e;
}
}

Prefer instanceof (or err.name when crossing realms) over comparing message strings.

Rethrowing with cause

Catch, add context, and rethrow so upper layers see one message while cause preserves the original for logs or retries.

rethrow.js
function doWork() {
try {
doFailSomeWay();
} catch (err) {
throw new Error("Failed in some way", { cause: err });
}
}
try {
doWork();
} catch (err) {
handleFailSomeWay(err.cause);
}

Re-throw unchanged errors you do not handle (throw e in a catch) so global handlers still run.

AggregateError

AggregateError groups multiple errors—Promise.any rejects with it when every input promise fails; each reason appears in errors.

aggregate.js
const failures = [new Error("a"), new Error("b")];
const agg = new AggregateError(failures, "All rejected");
console.log(agg.errors.length); // 2

Use it when your API performs parallel work and must report every failure, not only the first.

Async errors

Promises reject with whatever you pass to reject or throw inside an async executor. async/await maps rejections to catch on the awaited promise or to try/catch around await.

async-errors.js
async function run() {
try {
await fetchBroken();
} catch (err) {
console.log("Caught", err.message);
}
}
fetchBroken().catch((err) => console.log("Caught", err.message));

Unhandled promise rejections behave like uncaught exceptions in Node and browsers—register process or window handlers in servers and apps.

This is the last chapter in Language Basics. Earlier material lives under JavaScript syntax at a glance.

Search articles

Type a word to search published articles.

Open the search page