{"record":{"id":"075e40d917f5d3d4","repo":"marmelab/react-admin","slug":"useeditcontroller-fetched-record-s-id-attribute","errorCode":null,"errorMessage":"useEditController: Fetched record's id attribute (${record.id}) must match the requested 'id' (${id})","messagePattern":"useEditController: Fetched record's id attribute \\((.+?)\\) must match the requested 'id' \\((.+?)\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/ra-core/src/controller/edit/useEditController.ts","lineNumber":147,"sourceCode":"            enabled:\n                (!isPendingAuthenticated && !isPendingCanAccess) ||\n                disableAuthentication,\n            onError: () => {\n                notify('ra.notification.item_doesnt_exist', {\n                    type: 'error',\n                });\n                redirect(redirectOnError, resource, id);\n            },\n            refetchOnReconnect: false,\n            refetchOnWindowFocus: false,\n            retry: false,\n            ...otherQueryOptions,\n        }\n    );\n\n    // eslint-disable-next-line eqeqeq\n    if (record && record.id && record.id != id) {\n        throw new Error(\n            `useEditController: Fetched record's id attribute (${record.id}) must match the requested 'id' (${id})`\n        );\n    }\n\n    const getResourceLabel = useGetResourceLabel();\n    const recordRepresentation = getRecordRepresentation(record);\n    const defaultTitle = translate(`resources.${resource}.page.edit`, {\n        id,\n        record,\n        recordRepresentation:\n            typeof recordRepresentation === 'string'\n                ? recordRepresentation\n                : '',\n        _: translate('ra.page.edit', {\n            name: getResourceLabel(resource, 1),\n            id,\n            record,\n            recordRepresentation:","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/marmelab/react-admin/blob/051f511bb0afb5ea565c2d3728bf4dab0a6fa5e0/packages/ra-core/src/controller/edit/useEditController.ts#L129-L165","documentation":"useEditController (used internally by <Edit> / useEditController) fetches a single record by the id taken from the route/props. After the data provider returns, it checks that the fetched record's own `id` attribute equals the requested id (loose equality). If the data provider returned a record with a different id, react-admin throws because rendering an edit form for the wrong record would silently corrupt data.","triggerScenarios":"Calling useEditController (or <Edit>) where the query for `id` returns a record whose `record.id` differs, e.g. a misconfigured dataProvider that ignores the requested id (getOne returning a cached or first record), a backend that looks up by another key, or string-vs-number routing ids resolved against the wrong resource.","commonSituations":"Custom dataProviders that cache aggressively and return the most recent record regardless of id; backends whose GET /posts/:id endpoint ignores the :id param and returns a default document; copying an <Edit> component between resources without changing the resource prop so the provider resolves a record from another table; tests mocking getOne with a hardcoded record id.","solutions":["Fix the dataProvider's getOne so it returns exactly the record whose id was requested (verify the backend endpoint honors the :id parameter).","Ensure the backend lookup key matches the id react-admin passes (check the route param / id parsing, e.g. parseInt on numeric ids).","Check test fixtures / mocked getOne responses return a record with the same id that the component requests.","Confirm you are not reusing an <Edit> or useEditController instance across resources without setting the correct `resource` prop."],"exampleFix":"// before: dataProvider ignores the requested id\ngetOne: (resource, params) => fetch(`${apiUrl}/${resource}`).then(...)\n// after: request the specific record by id\ngetOne: (resource, params) =>\n  fetch(`${apiUrl}/${resource}/${params.id}`).then(...)","handlingStrategy":"validation","validationCode":"// validate what the dataProvider will return for this id before rendering Edit\nconst record = await dataProvider.getOne(resource, { id }).then(r => r.data);\nif (record?.id != null && String(record.id) !== String(id)) {\n  throw new Error(`dataProvider returned record id ${record.id} for requested id ${id}`);\n}","typeGuard":"const isRecordForId = (record, id) =>\n  record != null && record.id != null && String(record.id) === String(id);","tryCatchPattern":"try {\n  render(<Edit id={id} resource={resource} />);\n} catch (e) {\n  if (String(e.message).includes(\"must match the requested 'id'\")) {\n    // report dataProvider bug / reload record\n  } else throw e;\n}","preventionTips":["Always implement getOne as `${apiUrl}/${resource}/${params.id}` so the id is honored server-side.","Test dataProvider.getOne with two different ids and assert the returned record ids differ accordingly.","Normalize id types (number vs string) consistently between routes, backend, and fixtures.","Never reuse mocked getOne responses across test ids."],"tags":["react-admin","data-provider","getone","id-mismatch"],"backgroundTag":"record-id-mismatch","analyzedSha":"051f511bb0afb5ea565c2d3728bf4dab0a6fa5e0","analyzedAt":"2026-08-30T02:28:14.926Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}