{"record":{"id":"64f6eb9181eb7fb5","repo":"marmelab/react-admin","slug":"the-dataprovider-threw-an-error-it-should-return","errorCode":null,"errorMessage":"The dataProvider threw an error. It should return a rejected Promise instead.","messagePattern":"The dataProvider threw an error\\. It should return a rejected Promise instead\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/ra-core/src/dataProvider/useDataProvider.ts","lineNumber":151,"sourceCode":"                                return logoutIfAccessDenied(error).then(\n                                    loggedOut => {\n                                        if (loggedOut)\n                                            return {\n                                                data: arrayReturnTypes.includes(\n                                                    type\n                                                )\n                                                    ? []\n                                                    : {},\n                                            };\n                                        throw error;\n                                    }\n                                );\n                            });\n                    } catch (e) {\n                        if (process.env.NODE_ENV !== 'production') {\n                            console.error(e);\n                        }\n                        throw new Error(\n                            'The dataProvider threw an error. It should return a rejected Promise instead.'\n                        );\n                    }\n                };\n            },\n        });\n    }, [dataProvider, logoutIfAccessDenied, queryClient]);\n\n    return dataProviderProxy;\n};\n\nconst isAbortError = error =>\n    error instanceof DOMException &&\n    (error as DOMException).name === 'AbortError';\n","sourceCodeStart":133,"sourceCodeEnd":166,"githubUrl":"https://github.com/marmelab/react-admin/blob/051f511bb0afb5ea565c2d3728bf4dab0a6fa5e0/packages/ra-core/src/dataProvider/useDataProvider.ts#L133-L166","documentation":"Data providers must communicate failures by returning a rejected Promise, not by throwing synchronously. When the proxied call throws synchronously, useDataProvider catches it, logs it in development, and rethrows this canonical error so the failure semantics stay uniform.","triggerScenarios":"A dataProvider method implementation using `throw new Error(...)` instead of `return Promise.reject(...)` or an async function that rejects; synchronous validation inside a non-async method.","commonSituations":"Custom data providers written with plain function syntax; mocks that throw synchronously in tests; auth-failure paths that throw before returning a promise.","solutions":["Make provider methods async so thrown errors become rejections","Replace `throw e` with `return Promise.reject(e)` in non-async methods","Wrap the method body in try/catch and rethrow inside async context"],"exampleFix":"// before\ngetOne: (resource, params) => {\n  if (!params.id) throw new Error('no id');\n  return fetch(...);\n},\n// after\ngetOne: async (resource, params) => {\n  if (!params.id) throw new Error('no id'); // async: becomes rejection\n  return fetch(...);\n},","handlingStrategy":"try-catch","validationCode":"const rejects = async (fn: (...a: any[]) => any) => {\n  try { await fn(); return false; } catch { return true; }\n};\n// in provider tests: assert(await rejects(() => dp.getOne('x', { id: null })))","typeGuard":"const returnsPromise = (fn: unknown): fn is (...args: any[]) => Promise<unknown> =>\n  typeof fn === 'function';\n// and always call via async so throws become rejections","tryCatchPattern":"// inside the provider, never throw synchronously\ngetOne: async (resource, params) => {\n  try {\n    return await fetchOne(resource, params);\n  } catch (e) {\n    throw e; // async => rejection, satisfies the contract\n  }\n}","preventionTips":["Write every dataProvider method as an async function","Add a lint/test rule asserting provider methods return promises","Never do synchronous validation with bare throw in non-async methods"],"tags":["react-admin","data-provider","promise","async","contract"],"backgroundTag":"sync-throw-in-async-api","analyzedSha":"051f511bb0afb5ea565c2d3728bf4dab0a6fa5e0","analyzedAt":"2026-08-30T02:28:14.926Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}