facebook/relay · error

MatchContainer: Expected `match` value to be an object or nu

Error message

MatchContainer: Expected `match` value to be an object or null/undefined.

What it means

MatchContainer renders results of @match/@moduleField-style loaded content and expects the `match` prop to be either null/undefined or an object carrying fragment-spread metadata. It throws synchronously during render when match is a non-object truthy value (string, number, boolean, etc.), guarding against passing raw data where a match pointer is required.

Source

Thrown at packages/react-relay/relay-hooks/MatchContainer.js:111

  ...
};

export type MatchContainerProps<
  TProps extends {...},
  TFallback extends React.Node,
> = {
  readonly fallback?: ?TFallback,
  readonly loader: (module: unknown) => component(...TProps),
  readonly match: ?MatchPointer | ?TypenameOnlyPointer,
  readonly props?: TProps,
};

component MatchContainer<
  TProps extends {...},
  TFallback extends React.Node | null,
>(...{fallback, loader, match, props}: MatchContainerProps<TProps, TFallback>) {
  if (match != null && typeof match !== 'object') {
    throw new Error(
      'MatchContainer: Expected `match` value to be an object or null/undefined.',
    );
  }
  // NOTE: the MatchPointer type has a $fragmentSpreads field to ensure that only
  // an object that contains a FragmentSpread can be passed. If the fragment
  // spread matches, then the metadata fields below (__id, __fragments, etc.)
  // will be present. But they can be missing if all the fragment spreads use
  // @module and none of the types matched. The cast here is necessary because
  // fragment Flow types don't describe metadata fields, only the actual schema
  // fields the developer selected.
  const {
    __id,
    __fragments,
    __fragmentOwner,
    __fragmentPropName,
    __module_component,
  } = (match as $FlowFixMe) ?? {};
  if (

View on GitHub (pinned to 668b1b85e0)

Solutions

  1. Pass the resolver's returned object (the value Relay provides for @match fields) as `match`, not a scalar
  2. Guard at the call site: only render MatchContainer when match is null or an object
  3. Verify the parent fragment uses @match correctly so Relay supplies a proper match value

Example fix

// before
<MatchContainer match={moduleKey} fallback={<Spinner />} />;

// after
<MatchContainer
  match={typeof moduleKey === 'object' ? moduleKey : null}
  fallback={<Spinner />}
/>;
Defensive patterns

Strategy: type-guard

Validate before calling

function validateMatch(match) {
  if (match != null && typeof match !== 'object') {
    throw new TypeError('match must be an object or null/undefined');
  }
  return match;
}
// usage: <MatchContainer match={validateMatch(value)} ... />

Type guard

const isMatchValue = (m) => m == null || (typeof m === 'object' && !Array.isArray(m));

Try / catch

try {
  render(<MatchContainer match={value} fallback={<Spinner />} />);
} catch (e) {
  if (String(e.message).startsWith('MatchContainer:')) {
    render(<Spinner />); // degrade gracefully on invalid match data
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Passing a primitive (string/number/boolean), array misuse, or some other non-object truthy value as the `match` prop of MatchContainer, e.g. <MatchContainer match={someString} .../> while match != null && typeof match !== 'object'.

Common situations: Wiring the wrong variable into `match` (e.g. the resolver's string key instead of the resolver result); a refactored resolver now returning a scalar; misusing MatchContainer outside a @match field context.

Related errors


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