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
- Pass the resolver's returned object (the value Relay provides for @match fields) as `match`, not a scalar
- Guard at the call site: only render MatchContainer when match is null or an object
- 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
- Only pass values read from Relay @match resolver results to MatchContainer
- Type the match prop strictly (MatchContainerProps) so primitives fail type-checking
- Add prop-type/TS checks at boundaries where resolver data crosses modules
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
- MatchContainer: Invalid 'match' value, expected an object th
- useRefetchableFragmentNode: Unexpected action type
- useRefetchableFragmentNode: Unexpected action type
AI-assisted analysis of facebook/relay@668b1b85e0 (2026-09-02).
Data as JSON: /api/errors/d02d980bc3902b5d.
Report an issue: GitHub.