{"record":{"id":"9738d5eb73fba096","repo":"facebook/relay","slug":"matchcontainer-invalid-match-value-expected-an","errorCode":null,"errorMessage":"MatchContainer: Invalid 'match' value, expected an object that has a '...SomeFragment' spread.","messagePattern":"MatchContainer: Invalid 'match' value, expected an object that has a '\\.\\.\\.SomeFragment' spread\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/react-relay/relay-hooks/MatchContainer.js","lineNumber":135,"sourceCode":"  // spread matches, then the metadata fields below (__id, __fragments, etc.)\n  // will be present. But they can be missing if all the fragment spreads use\n  // @module and none of the types matched. The cast here is necessary because\n  // fragment Flow types don't describe metadata fields, only the actual schema\n  // fields the developer selected.\n  const {\n    __id,\n    __fragments,\n    __fragmentOwner,\n    __fragmentPropName,\n    __module_component,\n  } = (match as $FlowFixMe) ?? {};\n  if (\n    (__fragmentOwner != null && typeof __fragmentOwner !== 'object') ||\n    (__fragmentPropName != null && typeof __fragmentPropName !== 'string') ||\n    (__fragments != null && typeof __fragments !== 'object') ||\n    (__id != null && typeof __id !== 'string')\n  ) {\n    throw new Error(\n      \"MatchContainer: Invalid 'match' value, expected an object that has a \" +\n        \"'...SomeFragment' spread.\",\n    );\n  }\n\n  const LoadedContainer =\n    __module_component != null ? loader(__module_component) : null;\n\n  const fragmentProps = useMemo(() => {\n    // TODO: Perform this transformation in RelayReader so that unchanged\n    // output of subscriptions already has a stable identity.\n    if (__fragmentPropName != null && __id != null && __fragments != null) {\n      const fragProps: {\n        [string]: {\n          __fragmentOwner: $FlowFixMe,\n          __fragments: $FlowFixMe,\n          __id: string,\n        },","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/facebook/relay/blob/668b1b85e06261aa3b58dabfc51f8b5524a70955/packages/react-relay/relay-hooks/MatchContainer.js#L117-L153","documentation":"When `match` is an object, MatchContainer destructures Relay-injected metadata fields (__fragmentOwner, __fragmentPropName, __fragments, __id) added by fragment spreads. If any of those fields exist but has the wrong type — e.g. __id is present but not a string — the value was not produced by a real fragment spread, so the container throws. This validates that `match` came from Relay's fragment machinery rather than arbitrary user data.","triggerScenarios":"Passing a plain object as `match` that lacks proper fragment-spread metadata, or an object whose __fragmentOwner/__fragments/__id/__fragmentPropName fields were hand-crafted with wrong types (e.g. __id as a number, __fragments as a string).","commonSituations":"Mocking resolver/match data for tests with hand-made objects missing correct metadata fields; serializing and deserializing match values (striping/dropping metadata); constructing match objects manually instead of reading them from a Relay fragment result.","solutions":["Obtain `match` from reading a Relay fragment (useFragment/useClientFragment) on an @match field so all metadata fields are present and correctly typed","Fix test mocks to include correctly typed __id (string), __fragments (object), __fragmentOwner (object), __fragmentPropName (string)","Do not manually construct match objects; pass through the value Relay supplies"],"exampleFix":"// before\n<MatchContainer match={{ __id: 123 }} />;\n\n// after\nconst match = useFragment(graphql`fragment M on Resolver { ... }`, key);\n<MatchContainer match={match} />;","handlingStrategy":"type-guard","validationCode":"function validateMatchMetadata(match) {\n  if (match == null) return match;\n  const { __fragmentOwner: o, __fragmentPropName: p, __fragments: f, __id: id } = match;\n  const bad =\n    (o != null && typeof o !== 'object') ||\n    (p != null && typeof p !== 'string') ||\n    (f != null && typeof f !== 'object') ||\n    (id != null && typeof id !== 'string');\n  if (bad) throw new TypeError('match is not a valid fragment-spread value');\n  return match;\n}","typeGuard":"const hasValidSpreadMetadata = (m) =>\n  m == null || (\n    (m.__fragmentOwner == null || typeof m.__fragmentOwner === 'object') &&\n    (m.__fragmentPropName == null || typeof m.__fragmentPropName === 'string') &&\n    (m.__fragments == null || typeof m.__fragments === 'object') &&\n    (m.__id == null || typeof m.__id === 'string')\n  );","tryCatchPattern":"try {\n  render(<MatchContainer match={value} />);\n} catch (e) {\n  if (String(e.message).includes(\"Invalid 'match' value\")) {\n    console.warn('Discarding invalid match value', value);\n    render(<Spinner />);\n  } else {\n    throw e;\n  }\n}","preventionTips":["Never hand-construct match objects; always pass Relay fragment results through","Fix test mocks to include correctly typed __id/__fragments/__fragmentOwner/__fragmentPropName","Avoid serializing match values across boundaries (e.g. server->client) where metadata can be dropped"],"tags":["react","react-relay","render-time","runtime","type-mismatch"],"backgroundTag":"invalid-fragment-spread","analyzedSha":"668b1b85e06261aa3b58dabfc51f8b5524a70955","analyzedAt":"2026-09-02T19:57:20.783Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}