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

  1. Log/check `response.status` context — fix the server endpoint for that loader path
  2. Verify the route path/export name of the loader matches the route
  3. Test the loader URL directly (curl) with the same headers/credentials
  4. 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

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


AI-assisted analysis of expo/expo@7da61120be (2026-09-09). Data as JSON: /api/errors/982c87b57d1f71f5. Report an issue: GitHub.