JavaScript Cheatsheet

Error Handling

Use this JavaScript reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

try / catch / finally

try {
  riskyOperation();
  JSON.parse(badJson);
} catch (err) {
  console.error(err.message);
} finally {
  // Runs whether or not an error occurred
  cleanup();
}

// catch and finally are both optional, but one must be present
try { op(); } finally { cleanup(); }  // re-throw after cleanup
try { op(); } catch { /* ES2019 — optional binding */ }

finally behavior

function example() {
  try {
    return "try";
  } finally {
    return "finally"; // overrides try's return value
  }
}
example(); // "finally"

// finally runs even if catch rethrows
function safe() {
  try {
    throw new Error("oops");
  } catch (err) {
    throw err;  // re-throw
  } finally {
    console.log("this always runs"); // runs before re-throw propagates
  }
}

Error Types

// Base class
new Error("message")

// Built-in subtypes
new TypeError("Expected a string")      // wrong type
new RangeError("Must be between 0-100") // out of valid range
new ReferenceError("x is not defined")  // undefined variable
new SyntaxError("Unexpected token")     // parse error
new URIError("Bad URI")                 // encodeURI etc.
new EvalError("...")                    // eval issues (rare)

// AggregateError (ES2021) — multiple errors
new AggregateError([err1, err2], "Multiple failures")
// .errors property contains the array

// Error properties
const err = new Error("Something failed");
err.message    // "Something failed"
err.name       // "Error" (or "TypeError" etc.)
err.stack      // stack trace string (non-standard but universal)
err.cause      // ES2022 — original error (if provided)

// ES2022: error cause
new Error("Fetch failed", { cause: originalError });

Custom Error Classes

class AppError extends Error {
  constructor(message, code, statusCode = 500) {
    super(message);
    this.name = "AppError";
    this.code = code;
    this.statusCode = statusCode;
    // Maintain proper stack trace (V8)
    if (Error.captureStackTrace) {
      Error.captureStackTrace(this, AppError);
    }
  }
}

class ValidationError extends AppError {
  constructor(field, message) {
    super(message, "VALIDATION_ERROR", 400);
    this.name = "ValidationError";
    this.field = field;
  }
}

class NotFoundError extends AppError {
  constructor(resource) {
    super(`${resource} not found`, "NOT_FOUND", 404);
    this.name = "NotFoundError";
    this.resource = resource;
  }
}

// Usage
try {
  throw new ValidationError("email", "Invalid email format");
} catch (err) {
  if (err instanceof ValidationError) {
    console.log(err.field, err.message); // "email" "Invalid email format"
  } else if (err instanceof AppError) {
    console.log(err.statusCode, err.code);
  } else {
    throw err; // rethrow unknown errors
  }
}

Type-Based Error Handling

try {
  riskyOp();
} catch (err) {
  if (err instanceof TypeError) {
    console.error("Type error:", err.message);
  } else if (err instanceof RangeError) {
    console.error("Range error:", err.message);
  } else if (err?.name === "AbortError") {
    console.log("Request was aborted");
  } else {
    throw err; // always rethrow unrecognized errors
  }
}

Async Error Handling

async/await

async function fetchData(url) {
  try {
    const res = await fetch(url);
    if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);
    return await res.json();
  } catch (err) {
    if (err instanceof SyntaxError) throw new Error("Invalid JSON response");
    throw err; // propagate other errors
  }
}

// Caller
try {
  const data = await fetchData("/api/items");
} catch (err) {
  console.error("Failed:", err.message);
}

// Inline catch for optional failure
const data = await fetchData("/api/items").catch(() => null);

Promise chains

fetch("/api/users")
  .then(res => {
    if (!res.ok) throw new Error(`HTTP error ${res.status}`);
    return res.json();
  })
  .then(users => process(users))
  .catch(err => {
    // Catches errors from ALL preceding .then()
    console.error("Pipeline failed:", err);
  })
  .finally(() => setLoading(false));

// Handle errors per-step
fetch(url)
  .then(res => res.json().catch(() => ({}))) // fallback on bad JSON
  .catch(err => {
    console.error("Fetch failed:", err);
    return null; // recover — chain continues with null
  })
  .then(data => { if (data) process(data); });

Unhandled Rejections

// Browser
window.addEventListener("unhandledrejection", event => {
  console.error("Unhandled promise rejection:", event.reason);
  event.preventDefault(); // suppress default browser logging
});

// Node.js
process.on("unhandledRejection", (reason, promise) => {
  console.error("Unhandled rejection:", reason);
  process.exit(1);
});

process.on("uncaughtException", (err) => {
  console.error("Uncaught exception:", err);
  process.exit(1);
});

Error Propagation Patterns

// Re-throw with context
async function getUserPosts(userId) {
  try {
    const user = await fetchUser(userId);
    return await fetchPosts(user.id);
  } catch (err) {
    // Add context, preserve cause
    throw new Error(`Failed to get posts for user ${userId}`, { cause: err });
  }
}

// Unwrap cause chain
function getRootCause(err) {
  return err.cause ? getRootCause(err.cause) : err;
}

// Never swallow errors silently
try {
  op();
} catch (err) {
  // BAD: empty catch — hides bugs
}

// At minimum, log it
try {
  op();
} catch (err) {
  console.error(err);
  // then decide: recover, rethrow, or return fallback
}

Result Pattern (functional error handling)

// Return { ok, value } / { ok: false, error } instead of throwing
function safeJsonParse(str) {
  try {
    return { ok: true, value: JSON.parse(str) };
  } catch (err) {
    return { ok: false, error: err };
  }
}

const result = safeJsonParse('{"a":1}');
if (result.ok) {
  console.log(result.value);
} else {
  console.error(result.error.message);
}

// Async version
async function safeAsync(fn) {
  try {
    return { ok: true, value: await fn() };
  } catch (error) {
    return { ok: false, error };
  }
}

const { ok, value, error } = await safeAsync(() => fetchUser(id));

Assertions and Invariants

// Simple assert
function assert(condition, message) {
  if (!condition) throw new Error(`Assertion failed: ${message}`);
}

assert(typeof name === "string", "name must be a string");
assert(age >= 0, "age must be non-negative");

// Type guards
function requireString(val, name = "value") {
  if (typeof val !== "string") throw new TypeError(`${name} must be a string, got ${typeof val}`);
  return val;
}

// Exhaustive check pattern
function assertNever(x) {
  throw new Error(`Unexpected value: ${x}`);
}

function handleStatus(status) {
  switch (status) {
    case "active":   return doActive();
    case "inactive": return doInactive();
    default: assertNever(status); // TypeScript also catches this at compile time
  }
}

Global Error Handling

// Catch all synchronous errors
window.onerror = function(message, source, lineno, colno, error) {
  console.error(message, error);
  return true; // suppress default browser error
};

// Or use addEventListener
window.addEventListener("error", event => {
  console.error(event.error);
});

// Resource load errors (doesn't bubble — use capture)
window.addEventListener("error", event => {
  if (event.target !== window) {
    console.error("Resource failed to load:", event.target.src || event.target.href);
  }
}, true /* capture */);