{"record":{"id":"9bc5468fb63fc686","repo":"marmelab/react-admin","slug":"the-countheader-header-is-missing-in-the-http-r","errorCode":null,"errorMessage":"The ${countHeader} header is missing in the HTTP Response. The simple REST data provider expects responses for lists of resources to contain this header with the total number of results to build the pagination. If you are using CORS, did you declare ${countHeader} in the Access-Control-Expose-Headers header?","messagePattern":"The (.+?) header is missing in the HTTP Response\\. The simple REST data provider expects responses for lists of resources to contain this header with the total number of results to build the pagination\\. If you are using CORS, did you declare (.+?) in the Access-Control-Expose-Headers header\\?","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/ra-data-simple-rest/src/index.ts","lineNumber":75,"sourceCode":"        };\n        if (params.meta && params.meta.embed) {\n            query.embed = JSON.stringify(params.meta.embed);\n        }\n        const url = `${apiUrl}/${resource}?${stringify(query)}`;\n        const options =\n            countHeader === 'Content-Range'\n                ? {\n                      // Chrome doesn't return `Content-Range` header if no `Range` is provided in the request.\n                      headers: new Headers({\n                          Range: `${resource}=${rangeStart}-${rangeEnd}`,\n                      }),\n                      signal: params?.signal,\n                  }\n                : { signal: params?.signal };\n\n        return httpClient(url, options).then(({ headers, json }) => {\n            if (!headers.has(countHeader)) {\n                throw new Error(\n                    `The ${countHeader} header is missing in the HTTP Response. The simple REST data provider expects responses for lists of resources to contain this header with the total number of results to build the pagination. If you are using CORS, did you declare ${countHeader} in the Access-Control-Expose-Headers header?`\n                );\n            }\n            return {\n                data: json,\n                total:\n                    countHeader === 'Content-Range'\n                        ? parseInt(\n                              headers.get('content-range')!.split('/').pop() ||\n                                  '',\n                              10\n                          )\n                        : parseInt(headers.get(countHeader.toLowerCase())!),\n            };\n        });\n    },\n\n    getOne: async (resource, params) => {","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/marmelab/react-admin/blob/051f511bb0afb5ea565c2d3728bf4dab0a6fa5e0/packages/ra-data-simple-rest/src/index.ts#L57-L93","documentation":"The simple REST data provider's getList reads the total count of records from a response header (Content-Range by default, configurable via httpLink/countHeader). If the response lacks that header, pagination cannot be built, so it throws. This is almost always a server or CORS configuration issue, not a client bug.","triggerScenarios":"Calling dataProvider.getList() when the backend response for the collection endpoint does not include the count header (e.g. missing Content-Range), or the header is present server-side but stripped by the browser because it was not listed in Access-Control-Expose-Headers.","commonSituations":"Cross-origin APIs where the server sends Content-Range but does not expose it via CORS; backends (e.g. Express, Rails, API Platform misconfigured) that never send Content-Range for list endpoints; using a custom countHeader name (via httpLink customHeaderName option) that the server does not actually send.","solutions":["Make the server send the count header (e.g. Content-Range: posts 0-9/42) on list endpoints","If the API is cross-origin, add the header to Access-Control-Expose-Headers in the server's CORS config","If your API uses a different header name, configure it: simpleRestProvider(url, httpClient, 'X-Total-Count')"],"exampleFix":"// before (Express)\nres.set('Content-Range', `posts ${start}-${end}/${total}`);\n// after (also expose for CORS)\nres.set('Content-Range', `posts ${start}-${end}/${total}`);\nres.set('Access-Control-Expose-Headers', 'Content-Range');","handlingStrategy":"validation","validationCode":"// Probe the endpoint before wiring the provider\nconst res = await fetch(`${apiUrl}/posts?range=${encodeURIComponent('[0,9]')}`);\nif (!res.headers.has('Content-Range')) {\n    console.warn('Server does not send Content-Range; configure countHeader or fix server/CORS');\n}","typeGuard":"const hasCountHeader = (headers: Headers, name = 'Content-Range'): boolean =>\n    headers.has(name);","tryCatchPattern":"try {\n    const data = await dataProvider.getList('posts', { pagination: { page: 1, perPage: 10 } });\n} catch (e) {\n    if (e instanceof Error && e.message.includes('header is missing')) {\n        notify('Server does not expose the total count header; fix server/CORS config', { type: 'error' });\n        return { data: [], total: 0 };\n    }\n    throw e;\n}","preventionTips":["Verify list endpoints send Content-Range (or your custom countHeader) with curl -i","Always add the count header to Access-Control-Expose-Headers in CORS middleware","Match the third argument of simpleRestProvider to the header your API actually sends","Add an integration test asserting list responses include the count header"],"tags":["http","cors","pagination","rest"],"backgroundTag":"missing-pagination-header","analyzedSha":"051f511bb0afb5ea565c2d3728bf4dab0a6fa5e0","analyzedAt":"2026-08-30T02:28:14.926Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}