facebook/relay · warning

useRefetchableFragmentNode: Unexpected action type

Error message

useRefetchableFragmentNode: Unexpected action type

What it means

The internal reducer for useRefetchableFragmentNode switches over known action types and its default branch casts action.type to empty (an exhaustiveness check) and throws. At runtime this means an action with an unrecognized type was dispatched to the hook's internal state machine. Users should never see this; it indicates an internal invariant violation, a version mismatch between relay runtime packages, or corrupted/monkey-patched dispatch.

Source

Thrown at packages/react-relay/relay-hooks/legacy/useRefetchableFragmentNode.js:160

        onComplete: action.onComplete,
        refetchEnvironment: action.refetchEnvironment,
        refetchQuery: action.refetchQuery,
        renderPolicy: action.renderPolicy,
      };
    }
    case 'reset': {
      return {
        fetchPolicy: undefined,
        mirroredEnvironment: action.environment,
        mirroredFragmentIdentifier: action.fragmentIdentifier,
        onComplete: undefined,
        refetchQuery: null,
        renderPolicy: undefined,
      };
    }
    default: {
      action.type as empty;
      throw new Error('useRefetchableFragmentNode: Unexpected action type');
    }
  }
}

hook useRefetchableFragmentNode<
  TQuery extends OperationType,
  TKey extends ?{readonly $data?: unknown, ...},
>(
  fragmentNode: ReaderFragment,
  parentFragmentRef: unknown,
  componentDisplayName: string,
): ReturnType<TQuery, TKey, InternalOptions> {
  const parentEnvironment = useRelayEnvironment();
  const {refetchableRequest, fragmentRefPathInResponse} = getRefetchMetadata(
    fragmentNode,
    componentDisplayName,
  );
  const fragmentIdentifier = getFragmentIdentifier(

View on GitHub (pinned to 668b1b85e0)

Solutions

  1. Align all relay package versions (react-relay, relay-runtime, relay-test-utils) to the same release in package.json/lockfile
  2. Reinstall node_modules and rebuild to clear stale duplicated relay copies
  3. Check for custom middleware/monkey-patching that intercepts or fabricates actions for this hook
  4. If reproducible on matched versions, file an issue with relay with a minimal reproduction

Example fix

// before (partial upgrade)
"react-relay": "^14.0.0",
"relay-runtime": "^13.2.0"

// after
"react-relay": "14.0.0",
"relay-runtime": "14.0.0"
Defensive patterns

Strategy: try-catch

Validate before calling

// Before rendering, verify relay package versions match:
const reactRelayPkg = require('react-relay/package.json');
const runtimePkg = require('relay-runtime/package.json');
if (reactRelayPkg.version !== runtimePkg.version) {
  throw new Error(`relay version mismatch: react-relay ${reactRelayPkg.version} vs relay-runtime ${runtimePkg.version}`);
}

Type guard

const isKnownRefetchAction = (action) =>
  action != null && ['refetch', 'reset'].includes(action.type); // keep in sync with the hook's handled types

Try / catch

import { ErrorBoundary } from 'react-error-boundary';
<ErrorBoundary
  fallbackRender={({ error }) =>
    String(error.message).includes('useRefetchableFragmentNode: Unexpected action type')
      ? <FallbackUI />
      : <FatalError error={error} />
  }
>
  <ComponentUsingRefetchableFragment />
</ErrorBoundary>;

Prevention

When it happens

Trigger: The reducer receives an action whose type matches none of the handled cases — e.g. mixed relay package versions where one internal module dispatches actions another doesn't know, or custom code reaching into the hook's internals and dispatching a foreign action.

Common situations: Mismatched versions of react-relay vs relay-runtime after a partial upgrade; patched/bundled relay code where internal action creators changed; local forks or monkey-patches dispatching custom actions.

Related errors


AI-assisted analysis of facebook/relay@668b1b85e0 (2026-09-02). Data as JSON: /api/errors/62677d3dae914e6d. Report an issue: GitHub.