# Error Handling in JavaScript: Clean Code Best Practices from clean-code-javascript

> Master JavaScript error handling with clean code best practices. Learn to log, notify users, and report errors effectively to prevent silent failures and improve reliability.

- Repository: [Ryan McDermott/clean-code-javascript](https://github.com/ryanmcdermott/clean-code-javascript)
- Tags: best-practices
- Published: 2026-02-27

---

**Always handle errors explicitly by logging them to `console.error`, notifying the user, and reporting to monitoring services rather than silently swallowing exceptions.**

The `ryanmcdermott/clean-code-javascript` repository provides authoritative guidelines for writing maintainable JavaScript, dedicating specific sections to error handling patterns that prevent hidden bugs and debugging nightmares. According to the source code analysis of [`README.md`](https://github.com/ryanmcdermott/clean-code-javascript/blob/main/README.md) (lines 2003–2076), clean error handling in JavaScript revolves around two non-negotiable principles: never ignore caught errors, and never ignore rejected promises.

## Core Principles of Clean Error Handling

The guide establishes that error handling must be a first-class concern in your architecture. Silent failures are considered worse than crashes because they allow corrupted state to propagate through your application undetected.

The two foundational rules are:

1. **Never silently swallow errors** – Whether originating from a `try/catch` block or a rejected Promise, every error must be addressed.
2. **Make the error visible and actionable** – Log it for developers, surface it to users when appropriate, and report it to external monitoring services.

## Handling Synchronous Errors with try/catch

When wrapping code in `try { … } catch (e) { … }`, the presence of the `catch` clause signals that an error **might** occur. Simply logging with `console.log` or doing nothing loses the stack trace in production and eliminates your ability to react to the problem later.

According to [`README.md`](https://github.com/ryanmcdermott/clean-code-javascript/blob/main/README.md) lines 2009–2017, you should handle the error in a meaningful way:

- Write to `console.error` for better visibility in DevTools
- Notify the user via UI toasts, alerts, or error boundaries
- Report to a monitoring service like Sentry or New Relic

```javascript
function fetchUser(id) {
  try {
    // Might throw (e.g., JSON.parse on malformed data)
    const data = JSON.parse(database.get(id));
    return data;
  } catch (err) {
    console.error(err);           // 1️⃣ more visible than console.log
    notifyUserOfError(err);       // 2️⃣ UI feedback
    reportErrorToService(err);    // 3️⃣ telemetry
    // Optional: re-throw if you want upstream callers to react
    throw err;
  }
}

```

## Handling Asynchronous Errors in Promises

Promises that reject should be handled with `.catch()` (or a `try/catch` when using `await`). Ignoring a rejection is equivalent to swallowing a synchronous error. As noted in [`README.md`](https://github.com/ryanmcdermott/clean-code-javascript/blob/main/README.md) lines 2045–2048, the same three-step handling applies to asynchronous code.

### Promise chains with .catch()

```javascript
getData()
  .then(data => process(data))
  .catch(err => {
    console.error(err);           // 1️⃣
    notifyUserOfError(err);       // 2️⃣
    reportErrorToService(err);    // 3️⃣
  });

```

### Async/await with centralized error handling

```javascript
async function loadDashboard() {
  try {
    const stats = await fetchStats();   // may reject
    render(stats);
  } catch (err) {
    console.error(err);
    // Show a friendly message in the UI
    showToast('Unable to load dashboard. Please try again later.');
    // Send to monitoring
    reportErrorToService(err);
  }
}

```

## The Three-Step Error Handling Pattern

The `clean-code-javascript` guide implicitly recommends a consistent three-step pattern for every catch block:

1. **`console.error`** – Makes the error visible in development and production debugging tools, preserving stack traces.
2. **User notification** – Surfaces the failure in the UI so users know something went wrong rather than experiencing silent freezes.
3. **Service reporting** – Sends context to external monitoring (Sentry, Datadog, Rollbar) for aggregation and alerting.

You can DRY this pattern by creating a helper utility:

```javascript
// utils/error.js
export const handleError = err => {
  console.error(err);
  notifyUserOfError(err);
  reportErrorToService(err);
};

// usage
try {
  riskyOperation();
} catch (e) {
  handleError(e);
}

```

## Architectural Benefits of Explicit Error Handling

Adopting these patterns from `ryanmcdermott/clean-code-javascript` provides several architectural advantages:

- **Fail fast** – Throwing errors forces the runtime to stop the current call stack, making the failure point obvious rather than allowing undefined values to propagate.
- **Observable failures** – By logging or reporting, you turn silent failures into observable events that can be acted upon immediately.
- **Separation of concerns** – Error handling code stays centralized in helpers or catch blocks, keeping the happy-path logic uncluttered and focused on its single responsibility.

## Summary

- **Never silently swallow errors** in `try/catch` blocks or Promise rejections according to the `clean-code-javascript` guidelines.
- **Use `console.error`** instead of `console.log` to preserve stack traces and visibility.
- **Implement the three-step pattern**: log to console, notify the user, and report to a monitoring service.
- **Apply consistent handling** to both synchronous and asynchronous code using `.catch()` or `async/await` with `try/catch`.
- **Centralize error handling logic** in helper utilities to keep your codebase DRY and maintainable.

## Frequently Asked Questions

### What is the clean-code-javascript recommendation for handling errors?

The `ryanmcdermott/clean-code-javascript` repository recommends treating error handling as a first-class concern by never silently swallowing errors and always making them visible. According to [`README.md`](https://github.com/ryanmcdermott/clean-code-javascript/blob/main/README.md) lines 2003–2076, you should handle caught errors from `try/catch` blocks and rejected Promises by logging to `console.error`, notifying the user, and reporting to external monitoring services.

### Should I use console.log or console.error for errors in JavaScript?

You should use **`console.error`** for errors instead of `console.log`. The `clean-code-javascript` guide emphasizes that `console.error` provides better visibility in browser DevTools and Node.js environments, preserves stack traces more reliably, and allows filtering in production logging pipelines. Using `console.log` for errors makes debugging harder because it treats critical failures the same as informational messages.

### How should I handle errors in async/await functions?

Handle errors in `async/await` functions by wrapping awaited calls in **`try/catch` blocks**, applying the same three-step pattern used for synchronous errors. According to the source analysis of [`README.md`](https://github.com/ryanmcdermott/clean-code-javascript/blob/main/README.md) lines 2045–2048, you should catch the error, log it with `console.error`, notify the user if appropriate, and report it to a monitoring service. Never let Promise rejections bubble up unhandled, as this creates silent failures in asynchronous flows.

### Is it ever okay to silently catch and ignore an error?

No, it is **never okay** to silently catch and ignore an error according to the `clean-code-javascript` guidelines. Doing so eliminates your ability to debug the issue, prevents users from knowing something went wrong, and allows corrupted state to propagate through your application. If you truly must suppress an error, you should still log it with `console.error` and include a comment explaining why the error is being ignored for this specific edge case.