agentsclimarketplace

Promise unhandled rejection

Skill viditkbhatnagar/immunize/src/immunize/patterns/promise-unhandled-rejection

A curated pattern library that stops AI coding assistants from repeating common runtime errors. No API key. No LLM calls at runtime.

Install
npx -y skills add viditkbhatnagar/immunize --skill promise-unhandled-rejection

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use when writing JavaScript or TypeScript that returns a Promise chain (.then) or awaits a value, to ensure every rejection path has a handler before the rejection escapes the function.

SKILL.md

2.6 KB, as published. Nobody here has run it

promise-unhandled-rejection

Every Promise chain needs a terminal handler. Every await needs a surrounding try/catch (or a caller that has one). Rejections that escape both end up as:

UnhandledPromiseRejection
Uncaught (in promise) ...

Recent Node versions abort the process on unhandled rejections by default. Browsers log them to the console and silently drop the result — worse, because downstream code keeps running with corrupted state.

Promise chains: terminate in .catch()

Wrong — a rejected fetch escapes the chain:

export function fetchUserName(id) {
  return fetch(`/api/users/${id}`)
    .then((r) => r.json())
    .then((d) => d.name);
}

Right — .catch() translates the rejection into a typed error the caller can handle:

export function fetchUserName(id) {
  return fetch(`/api/users/${id}`)
    .then((r) => r.json())
    .then((d) => d.name)
    .catch((err) => {
      throw new Error(`failed to fetch user ${id}: ${err.message}`);
    });
}

async/await: wrap the awaitable in try/catch

Wrong — await re-raises into the async function's caller, which may not exist (top-level event handler, fire-and-forget):

async function syncUser(id) {
  const user = await fetchUserName(id);
  cache.set(id, user);
}

Right — handle the rejection at the boundary:

async function syncUser(id) {
  try {
    const user = await fetchUserName(id);
    cache.set(id, user);
  } catch (err) {
    logger.warn({ id, err }, "user sync failed");
  }
}

Don't return a chain from a fire-and-forget call

If you call something().then(...) and don't return or await the result, you've thrown the chain on the floor. Either:

  • return it so the caller's chain takes responsibility, or
  • await it inside a try, or
  • end with an explicit .catch() that handles or logs.

When global handlers are still useful

Even with disciplined per-chain handling, register process.on('unhandledRejection', ...) (Node) or window.addEventListener('unhandledrejection', ...) (browser) as a last-resort safety net for code paths you haven't found yet. Don't use the global handler as a substitute for local handling.

Immunity note

The verification scans the fixture for a Promise chain (.then() and asserts a corresponding .catch( exists. The repro is missing the handler and fails the assertion; the fix adds .catch( and passes.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.