Kodokon kodokon.com

Debugging and robustness: try/catch, advanced console, custom errors

Build an explicit error strategy with dedicated classes, well-placed catch boundaries, and a console used to its full potential.

9 min · 3 questions

Open this lesson in Kodokon

Robustness is not about catching every error, but about catching them in the right place. Field rule: fail fast and loudly as close to the bug as possible (validation, invariants), and only catch at the boundaries where you can actually react: show a message, retry, log. A try/catch that does nothing useful does not make the code safer; it makes failures silent.

JAVASCRIPT
function parseConfig(json) {
  try {
    return JSON.parse(json);
  } catch (error) {
    console.error("Unreadable config:", error.message);
    throw error; // rethrow: nothing can be fixed here
  } finally {
    console.log("Finished reading configuration");
  }
}
Log, then rethrow: the boundary above will decide.

Plain strings do not make an error strategy. Extend Error to create classes carrying structured data (HTTP status, invalid field) that can be discriminated with instanceof. Since ES2022, the cause option chains the original error to the high-level one: you expose a clear business message without losing the technical trail.

JAVASCRIPT
class ApiError extends Error {
  constructor(message, status, options) {
    super(message, options);
    this.name = "ApiError";
    this.status = status;
  }
}
try {
  const low = new Error("socket timeout");
  throw new ApiError("Profile unavailable", 503,
    { cause: low });
} catch (error) {
  if (error instanceof ApiError) {
    console.error(error.status, error.cause.message);
  }
}
A discriminable business error with the technical cause preserved.

The console offers much more than log: console.table lines up collections of objects, group/groupEnd nests output, time/timeEnd measures a duration, console.assert only logs when the condition is false, and console.trace prints the current call stack. Combined, these tools beat a downpour of anonymous log calls hands down.

JAVASCRIPT
const users = [
  { name: "Ada", role: "admin" },
  { name: "Linus", role: "dev" },
];
console.table(users);
console.group("Checks");
console.assert(users.length > 0, "Empty list");
console.time("sort");
users.sort((a, b) => a.name.localeCompare(b.name));
console.timeEnd("sort");
console.groupEnd();
Structured output: table, group, assertion and timer.

Knowledge check

Make sure you remember the key points of this lesson.

  1. When does the finally block run?
    • Only if no error was thrown
    • Only if the catch was triggered
    • In every case, including after a return or a throw
  2. What is the cause option for when constructing an error?
    • To prevent the error from propagating
    • To attach the original error and preserve the technical context
    • To set the HTTP status code automatically
    • To trigger a new catch
  3. What is the behavior of console.assert(condition, message)?
    • It logs the message only if the condition is false
    • It throws an exception if the condition is false
    • It logs the message only if the condition is true