facebook/react · error · Error

600

600

Error message

A rejected Promise was passed to React without a `reason` property. React threw a generic error from where the Promise was used to assist in identifying the problematic Promise. Make sure that instrumented Promises correctly set the `reason` property when setting `status` to `'rejected'`.

What it means

React consumes instrumented promises synchronously via .status/.reason instead of attaching .then handlers. In the rejected branch, if reason is undefined and the object lacks a reason key entirely, React throws this generic error (code 600) at the consumption point — rethrowing a bare undefined would lose the callstack and make the offending promise untraceable.

Source

Thrown at packages/react-server/src/ReactFizzThenable.js:88

  switch (thenable.status) {
    case 'fulfilled': {
      // This could be a bad instrumentation that doesn't set .value.
      // We're not type-checking since this is a hot path where you can
      // track down easily when something becomes `undefined` unexpectedly.
      const fulfilledValue: T = thenable.value;
      return fulfilledValue;
    }
    case 'rejected': {
      const rejectedError = thenable.reason;

      // Rejected Promises are rarer so we're doing an extra type-check in
      // case of a bad instrumentation that doesn't set .reason
      // If we end up throwing `undefined` it becomes hard to track down
      // where that throw originated because no callstack would exist.
      // React would still have a Component stack but that could only be used
      // as an approximation.
      if (rejectedError === undefined && !('reason' in thenable)) {
        throw new Error(
          'A rejected Promise was passed to React without a `reason` property. ' +
            'React threw a generic error from where the Promise was used to assist in identifying the problematic Promise. ' +
            "Make sure that instrumented Promises correctly set the `reason` property when setting `status` to `'rejected'`.",
        );
      }

      throw rejectedError;
    }
    default: {
      if (typeof thenable.status === 'string') {
        // Only instrument the thenable if the status if not defined. If
        // it's defined, but an unknown value, assume it's been instrumented by
        // some custom userspace implementation. We treat it as "pending".
        // Attach a dummy listener, to ensure that any lazy initialization can
        // happen. Flight lazily parses JSON when the value is actually awaited.
        thenable.then(noop, noop);
      } else {
        const pendingThenable: PendingThenable<T> = thenable as any;

View on GitHub (pinned to eafeac097b)

Solutions

  1. When setting promise.status = 'rejected', always also set promise.reason = theError
  2. Update custom instrumentation helpers to follow the full status+reason contract
  3. Upgrade libraries that patch Promise instrumentation

Example fix

// before
function instrument(promise) {
  promise.status = 'rejected'; // no reason -> generic error later
  return promise;
}

// after
function instrument(promise, error) {
  promise.status = 'rejected';
  promise.reason = error;
  return promise;
}
Defensive patterns

Strategy: validation

Validate before calling

function assertInstrumented(promise) {
  if (promise.status === 'rejected' && !('reason' in promise)) {
    throw new TypeError('Rejected promise is missing .reason');
  }
  return promise;
}

Prevention

When it happens

Trigger: A promise or thenable passed to use() (or thrown to suspend) whose instrumentation set status = 'rejected' without also setting reason; custom suspense helpers, promise patches, or mocks that set status only.

Common situations: Hand-rolled promise instrumentation following older React recipes; test doubles that set promise.status; a library patching global Promise to add status but not reason.

Related errors


AI-assisted analysis of facebook/react@eafeac097b (2026-08-21). Data as JSON: /api/errors/8db14f5f8108d515. Report an issue: GitHub.