← CodeClarityLab Home
Browse by Category
+ added · updated 7d
← Back to glossary

Async Error Handling (try/catch + Promise)

javascript ES2017 Intermediate
debt(d7/e3/b5/t7)
d7 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'only careful code review or runtime testing' (d7). ESLint can flag some patterns (e.g. no-floating-promises with TypeScript plugin), but the core problem — a non-awaited Promise inside an async function or a fire-and-forget call — is not caught by default ESLint rules. TypeScript alone doesn't prevent it either. Unhandled rejections in browsers are silent; in Node <15 they were silent in production. Detection typically requires careful review or runtime monitoring.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix indicates wrapping async bodies in try/catch, adding .catch() to all chains, and adding a global unhandledRejection handler. This is a small but repeated pattern fix — not a single one-liner (e1), but not a cross-cutting refactor (e5+). Each missing await or missing .catch() is a local fix, but the pattern may need to be applied across multiple async functions.

b5 Burden Structural debt — long-term weight of choosing wrong

Closest to 'persistent productivity tax' (b5). Async error handling applies broadly to both web and CLI contexts and affects every async operation in the codebase. Developers must consistently remember the pattern for every new async function they write, slowing down many work streams and requiring ongoing discipline, but it doesn't fundamentally reshape the architecture (not b7/b9).

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7). The misconception field states explicitly: developers believe try/catch in an async function catches all errors, but it doesn't catch errors in non-awaited Promises or in setTimeout callbacks. This contradicts the intuitive mental model of try/catch as a universal error boundary in synchronous code, and the 'obvious' pattern (wrap in try/catch and assume safety) is commonly wrong — aligning with a serious trap score.

About DEBT scoring →

TL;DR

Errors in async functions require try/catch or .catch() — a rejected Promise that's not caught becomes an unhandledRejection that crashes Node or shows a console warning in browsers.

Explanation

In async/await, try/catch catches errors from awaited Promises. But: errors in Promise chains without .catch() become unhandled rejections. async functions always return Promises — their errors are rejections, not synchronous throws. Common patterns: wrap entire async function in try/catch for simplicity, use .catch() on Promise chains, add global process.on('unhandledRejection') in Node as last resort. Parallel operations: await Promise.all() — if any rejects, the others still run but their results are lost. Use Promise.allSettled() to handle all outcomes.

Common Misconception

try/catch in an async function catches all errors — it doesn't catch errors in non-awaited Promises or in callbacks passed to setTimeout.

Why It Matters

Unhandled Promise rejections crash Node.js processes (since Node 15) and silently fail in browsers — proper async error handling is critical for reliability.

Common Mistakes

  • Not awaiting a Promise inside async function — rejection goes unhandled.
  • Using try/catch but forgetting to await the operation.
  • Not using Promise.allSettled() when you need all results regardless of failures.

Code Examples

✗ Vulnerable
async function loadData() {
    fetch('/api/data'); // Not awaited — rejection unhandled
    const result = await processData();
}

// Promise chain without .catch():
fetchUser().then(processUser); // Rejection unhandled
✓ Fixed
async function loadData() {
    try {
        const response = await fetch('/api/data');
        const data = await response.json();
        return data;
    } catch (error) {
        console.error('Load failed:', error);
        throw error; // Re-throw for caller to handle
    }
}

// Promise chain with .catch():
fetchUser()
    .then(processUser)
    .catch(err => logger.error(err));

Added 22 Mar 2026
Edited 5 Apr 2026
Views 24
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
1 ping F 0 pings S 1 ping S 0 pings M 0 pings T 0 pings W 0 pings T 1 ping F 0 pings S 0 pings S 1 ping M 0 pings T 0 pings W 0 pings T 1 ping F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 1 ping T 1 ping F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 1 ping F 0 pings S
No pings yet today
Amazonbot 7 Perplexity 4 Google 3 Unknown AI 3 Majestic 1 Ahrefs 1
crawler 17 crawler_json 1 pre-tracking 1
DEV INTEL Tools & Severity
🟠 High ⚙ Fix effort: Medium
⚡ Quick Fix
Wrap async function bodies in try/catch. Add .catch() to all Promise chains. Add process.on('unhandledRejection') in Node. Never fire-and-forget Promises.
📦 Applies To
javascript ES2017 web cli
🔗 Prerequisites
🔍 Detection Hints
async function|await
Auto-detectable: ✓ Yes eslint typescript
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Medium ✗ Manual fix Fix: Medium Context: Function Tests: Update
CWE-390 CWE-755

✓ schema.org compliant