marmelab/react-admin · error · Error
The dataProvider threw an error. It should return a rejected
Error message
The dataProvider threw an error. It should return a rejected Promise instead.
What it means
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.
Source
Thrown at packages/ra-core/src/dataProvider/useDataProvider.ts:151
return logoutIfAccessDenied(error).then(
loggedOut => {
if (loggedOut)
return {
data: arrayReturnTypes.includes(
type
)
? []
: {},
};
throw error;
}
);
});
} catch (e) {
if (process.env.NODE_ENV !== 'production') {
console.error(e);
}
throw new Error(
'The dataProvider threw an error. It should return a rejected Promise instead.'
);
}
};
},
});
}, [dataProvider, logoutIfAccessDenied, queryClient]);
return dataProviderProxy;
};
const isAbortError = error =>
error instanceof DOMException &&
(error as DOMException).name === 'AbortError';
View on GitHub (pinned to 051f511bb0)
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
Example fix
// before
getOne: (resource, params) => {
if (!params.id) throw new Error('no id');
return fetch(...);
},
// after
getOne: async (resource, params) => {
if (!params.id) throw new Error('no id'); // async: becomes rejection
return fetch(...);
}, Defensive patterns
Strategy: try-catch
Validate before calling
const rejects = async (fn: (...a: any[]) => any) => {
try { await fn(); return false; } catch { return true; }
};
// in provider tests: assert(await rejects(() => dp.getOne('x', { id: null }))) Type guard
const returnsPromise = (fn: unknown): fn is (...args: any[]) => Promise<unknown> => typeof fn === 'function'; // and always call via async so throws become rejections
Try / catch
// inside the provider, never throw synchronously
getOne: async (resource, params) => {
try {
return await fetchOne(resource, params);
} catch (e) {
throw e; // async => rejection, satisfies the contract
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: Custom data providers written with plain function syntax; mocks that throw synchronously in tests; auth-failure paths that throw before returning a promise.
Related errors
- Invalid dataProvider response for create: missing id
- Unknown dataProvider function: ${type}
- useEditController: Fetched record's id attribute (${record.i
- useShowController: Fetched record's id attribute (${record.i
- create is not implemented
AI-assisted analysis of marmelab/react-admin@051f511bb0 (2026-08-30).
Data as JSON: /api/errors/64f6eb9181eb7fb5.
Report an issue: GitHub.