{"record":{"id":"982c87b57d1f71f5","repo":"expo/expo","slug":"failed-to-fetch-loader-data-response-status","errorCode":null,"errorMessage":"Failed to fetch loader data: ${response.status}","messagePattern":"Failed to fetch loader data: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/expo-router/src/loaders/utils.ts","lineNumber":48,"sourceCode":" * This works in all environments including:\n * 1. Development with Metro dev server\n * 2. Production with static files (SSG)\n * 3. SSR environments\n *\n * @see import('packages/@expo/cli/src/start/server/metro/createServerRouteMiddleware.ts').createRouteHandlerMiddleware\n * @see import('packages/expo-server/src/vendor/environment/common.ts').createEnvironment\n */\nexport async function fetchLoader(routePath: string, requestInit: RequestInit = {}): Promise<any> {\n  let loaderPath = getLoaderModulePath(routePath);\n  if (__DEV__ && devLoaderCacheRevision > 0) {\n    loaderPath += `${loaderPath.includes('?') ? '&' : '?'}_expo_loader_v=${devLoaderCacheRevision}`;\n  }\n\n  const headers = new Headers(requestInit.headers);\n  headers.set('Accept', 'application/json');\n  const response = await fetch(loaderPath, { ...requestInit, headers });\n  if (!response.ok) {\n    throw new Error(`Failed to fetch loader data: ${response.status}`);\n  }\n\n  try {\n    return await response.json();\n  } catch (error) {\n    throw new Error(`Failed to parse loader data: ${error}`);\n  }\n}\n","sourceCodeStart":30,"sourceCodeEnd":57,"githubUrl":"https://github.com/expo/expo/blob/7da61120be99bcd9cc2e5dbdba6d803c21b5c68b/packages/expo-router/src/loaders/utils.ts#L30-L57","documentation":"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.","triggerScenarios":"`await fetch(loaderPath, ...)` returns `response.ok === false` — the loader endpoint doesn't exist, returned an error status, or the server rejected the request.","commonSituations":"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.","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"],"exampleFix":"// before\nconst data = await fetchLoader(req);\n// after\ntry { const data = await fetchLoader(req); } catch (e) { showErrorBoundary(e); }","handlingStrategy":"retry","validationCode":"const res = await fetch(loaderPath, { method: 'HEAD' });\nif (!res.ok) console.warn(`loader endpoint unavailable: ${res.status}`);","typeGuard":"function isOkResponse(r: Response): boolean { return r.ok; }","tryCatchPattern":"try { data = await fetchLoader(req); }\ncatch (e) { if (String(e).startsWith('Failed to fetch loader data')) { data = fallbackData; /* optionally retry with backoff */ } else throw e; }","preventionTips":["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"],"tags":["network","http","expo-router","loaders"],"backgroundTag":"http-error-response","analyzedSha":"7da61120be99bcd9cc2e5dbdba6d803c21b5c68b","analyzedAt":"2026-09-09T16:01:44.845Z","contentChangedAt":"2026-09-09T16:01:44.845Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}