Promise.reject()

The asymmetric twin of Promise.resolve. resolve inspects its argument and may pass it through; reject takes the argument literally, whatever it is.

Promise static methodES2015
Common call
Promise.reject(new Error("nope"))
Returns
an already-rejected promise
Replaces
new Promise((_, rej) => rej(e))
Watch out
unhandled unless something catches it THIS tick
Promise.reject(reasonreason — Anything, though it should be an Error. Unlike resolve, a promise or thenable here is NOT adopted — it becomes the rejection reason itself.type: any · default: undefined)
→ Promise

Parameters

NameTypeRequiredDescription
reasonanyno (undefined)Anything, though it should be an Error. Unlike resolve, a promise or thenable here is NOT adopted — it becomes the rejection reason itself.

Return value

Promise — A Promise rejected with exactly the reason given. No unwrapping and no adoption — passing a promise makes the REASON a promise.

Common patterns

Reject with an Error
Always an Error, for the stack trace.
return Promise.reject(new Error("not found"));
Build a timeout
The rejecting half of a race.
const timeout = ms => new Promise((_, rej) =>
  setTimeout(() => rej(new Error("timeout")), ms));
Throwing is usually clearer
Inside async, a throw produces the same thing.
async function f() { throw new Error("nope"); }

Examples

1. A rejected promise
await Promise.reject(new Error("x")).catch(e => e.message)
Returns
'x'
2. It does NOT unwrap
Promise.reject(Promise.resolve(1)).catch(v => v instanceof Promise)
Returns
true
3. resolve DOES pass through
const p = Promise.resolve(1); Promise.resolve(p) === p
Returns
true
4. Any value works
await Promise.reject("just a string").catch(v => typeof v)
Returns
'string'
5. Unhandled if nothing catches
Promise.reject(new Error("x"))
Returns
UnhandledPromiseRejection
6. An async throw is equivalent
async function f() { throw new Error("x"); }
Returns
a rejected promise

Pitfalls

1. It does not unwrap a promise
The asymmetry with resolve. Promise.reject(somePromise) produces a rejection whose REASON is that promise, so a catch handler receives a Promise object rather than an error — and awaiting it inside the handler is the only way to see what went wrong.
Reason is a promise
Promise.reject(fetchData()).catch(v => v instanceof Promise)
true
Await, then rethrow
try { await fetchData(); } catch (e) { throw e; }
a real error
2. Rejecting with a non-Error loses the stack
Rejecting with a string or an object works and gives you no stack trace, no name and nothing for a logger to format. It is the promise equivalent of throwing a string, and just as unhelpful at three in the morning.
No stack
Promise.reject("failed")
reason is the string 'failed'
An Error
Promise.reject(new Error("failed"))
name, message and stack
3. Creating one eagerly can go unhandled
A rejected promise stored in a variable and caught later may already have triggered an unhandled-rejection warning, because the check happens when the microtask queue drains. Create the rejection at the point where it will be handled.
Warns first
const p = Promise.reject(new Error("x"));
await later();
p.catch(handle);
unhandled rejection reported
Attach immediately
const p = Promise.reject(new Error("x")).catch(handle);
handled
4. In an async function, throw instead
Returning Promise.reject from an async function works but reads oddly and skips the stack capture a throw gives you at the right line. throw is the idiomatic form, and try/catch can see it.
Roundabout
async function f() { return Promise.reject(new Error("x")); }
rejects
Idiomatic
async function f() { throw new Error("x"); }
rejects, with a better trace

When to use

Use it
  • Returning an early failure from a non-async function
  • The rejecting side of a timeout
  • Test doubles for a failing dependency
  • Converting a validation failure into a rejected promise at an API boundary
Reach for something else
  • Inside an async function → throw
  • You need external control → Promise.withResolvers
  • Rejecting with a promise → await it and rethrow the real error
  • Rejecting with a non-Error → you lose the stack

Notes

Complexity
O(1)
Return
A new rejected Promise
CPython impl
V8: Builtins-promise-reject
Memory
Allocates one promise
Thread-safe
Single-threaded; the unhandled-rejection check runs when the microtask queue drains

FAQ

Because awaiting Promise.reject(x) always throws x — a demo would simply echo whatever you typed back as an error, which demonstrates nothing. The behaviour worth knowing is that it does NOT unwrap a promise passed to it, and that an unhandled rejection is reported when the microtask queue drains; both are shown in the examples above, run in a real runtime.

History

ES2015
Promise.reject added with the constructor, deliberately without thenable adoption.
ES2017
async/await made throw the usual way to produce a rejection.