expo/expo · error
Failed to fetch loader data: ${response.status}
Error message
Failed to fetch loader data: ${response.status} What it means
fetchLoader requested the loader data module for a route (GET of the loader path, with a dev cache-busting query when applicable) and the HTTP response was not ok (4xx/5xx). The fetch itself succeeded but the server rejected or could not serve the loader module — typically a 404 when the route has no loader entry, or a 500 from the server route middleware. The status code is embedded in the message.
Source
Thrown at packages/expo-router/src/loaders/utils.ts:48
* This works in all environments including:
* 1. Development with Metro dev server
* 2. Production with static files (SSG)
* 3. SSR environments
*
* @see import('packages/@expo/cli/src/start/server/metro/createServerRouteMiddleware.ts').createRouteHandlerMiddleware
* @see import('packages/expo-server/src/vendor/environment/common.ts').createEnvironment
*/
export async function fetchLoader(routePath: string, requestInit: RequestInit = {}): Promise<any> {
let loaderPath = getLoaderModulePath(routePath);
if (__DEV__ && devLoaderCacheRevision > 0) {
loaderPath += `${loaderPath.includes('?') ? '&' : '?'}_expo_loader_v=${devLoaderCacheRevision}`;
}
const headers = new Headers(requestInit.headers);
headers.set('Accept', 'application/json');
const response = await fetch(loaderPath, { ...requestInit, headers });
if (!response.ok) {
throw new Error(`Failed to fetch loader data: ${response.status}`);
}
try {
return await response.json();
} catch (error) {
throw new Error(`Failed to parse loader data: ${error}`);
}
}
View on GitHub (pinned to 7da61120be)
Solutions
- Log/check `response.status` context — fix the server endpoint for that loader path
- Verify the route path/export name of the loader matches the route
- Test the loader URL directly (curl) with the same headers/credentials
- Handle non-2xx in app code: catch and render an error boundary or fallback UI
Example fix
// before
const data = await fetchLoader(req);
// after
try { const data = await fetchLoader(req); } catch (e) { showErrorBoundary(e); } Defensive patterns
Strategy: retry
Validate before calling
const res = await fetch(loaderPath, { method: 'HEAD' });
if (!res.ok) console.warn(`loader endpoint unavailable: ${res.status}`); Type guard
function isOkResponse(r: Response): boolean { return r.ok; } Try / catch
try { data = await fetchLoader(req); }
catch (e) { if (String(e).startsWith('Failed to fetch loader data')) { data = fallbackData; /* optionally retry with backoff */ } else throw e; } Prevention
- Return proper JSON error payloads from loader endpoints
- Check loader URL routing on the server/deployment config
- Handle 401/403 by refreshing credentials before fetching loader data
- Wrap loader fetches in retry with exponential backoff for transient 5xx
When it happens
Trigger: `await fetch(loaderPath, ...)` returns `response.ok === false` — the loader endpoint doesn't exist, returned an error status, or the server rejected the request.
Common situations: Loader API returning 500 due to server exception; route path mismatch between client and server (404); auth middleware returning 401/403; proxy or API route misconfiguration in deployment.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- EXPO_ROUTER_SERVER_MANIFEST
- LOADERS_NOT_ENABLED
- LOADER_EXECUTION_FAILED
- Invalid manifest: missing launchAsset
- Failed to download bundle: ${bundleRes.status}
AI-assisted analysis of expo/expo@7da61120be (2026-09-09).
Data as JSON: /api/errors/982c87b57d1f71f5.
Report an issue: GitHub.