{"record":{"id":"2e30213bbe7aa9d0","repo":"MiniMax-AI/skills","slug":"http-r-status","errorCode":null,"errorMessage":"HTTP ${r.status}","messagePattern":"HTTP \\$\\{r\\.status\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"skills/react-native-dev/references/engineering.md","lineNumber":204,"sourceCode":"    users.ts                User-related API calls\n    posts.ts                Post-related API calls\n  storage/\n    secure-store.ts         Wrapper for expo-secure-store\n    async-storage.ts        Wrapper for AsyncStorage\n  notifications/\n    push.ts                 Expo push notification helpers\n```\n\n```tsx\n// services/api/client.ts\nconst BASE_URL = process.env.EXPO_PUBLIC_API_URL!;\n\nexport const api = {\n  get: <T,>(path: string, token?: string) =>\n    fetch(`${BASE_URL}${path}`, {\n      headers: { Authorization: token ? `Bearer ${token}` : \"\" },\n    }).then(async (r) => {\n      if (!r.ok) throw new Error(`HTTP ${r.status}`);\n      return r.json() as Promise<T>;\n    }),\n  // post/put/delete follow same pattern — add method, Content-Type, JSON.stringify(body)\n};\n```\n\n### Monorepo\n\n```\nmy-monorepo/\n  apps/\n    mobile/                 Expo app (all native deps here)\n      package.json\n      app.json\n    web/                    Next.js app\n      package.json\n  packages/\n    ui/                     Shared UI components (no native deps)","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/MiniMax-AI/skills/blob/60aaae52bb2af8162732751a4332f62a5fef518b/skills/react-native-dev/references/engineering.md#L186-L222","documentation":"`if (!r.ok) throw new Error(`HTTP ${r.status}`)` inside the React Native `api.get` fetch wrapper. The Fetch API only rejects on network failures; HTTP error statuses (4xx/5xx) resolve as a normal Response with `r.ok === false`. This wrapper converts any non-2xx status into a thrown `Error` so callers' `.then` chains skip on server errors. The message carries the raw status code for diagnosis.","triggerScenarios":"Any request to `${EXPO_PUBLIC_API_URL}${path}` whose response status is not in the 200–299 range: 401 (bad/expired token), 404 (wrong path or BASE_URL), 422/400 (bad payload), 500/502/503 (server/gateway). Also an empty/missing `Authorization` header reaching a protected route.","commonSituations":"`EXPO_PUBLIC_API_URL` pointing at the wrong host/path, an expired or missing auth token passed as `undefined`, a backend route that changed, CORS preflight rejection surfacing as an opaque error, or a transient 502/503 during deploy.","solutions":["Read the status code from the message: 401 → refresh/re-login and resend with a valid token; 404 → verify BASE_URL and path; 5xx → retry or check backend health.","Confirm `EXPO_PUBLIC_API_URL` is set in `.env` and rebuild the Expo app (public env vars are inlined at build time, so changes need a rebuild).","Pass the token explicitly: `api.get<T>('/me', userToken)` instead of leaving it undefined on protected routes.","Wrap calls in try/catch and surface a user-facing message; for 401s, redirect to login."],"exampleFix":"// before\nconst me = await api.get<User>('/me'); // throws 'HTTP 401' if token missing\n\n// after\ntry {\n  const me = await api.get<User>('/me', session?.token);\n} catch (e) {\n  if (e instanceof Error && e.message === 'HTTP 401') router.replace('/login');\n  else throw e;\n}","handlingStrategy":"try-catch","validationCode":"// Pre-flight: ensure BASE_URL and token exist before calling.\nif (!process.env.EXPO_PUBLIC_API_URL) {\n  throw new Error('EXPO_PUBLIC_API_URL is not set');\n}\nconst token = await getToken();\nif (!token) { router.replace('/login'); return; }","typeGuard":"function isHttpError(e: unknown, status?: number): e is Error {\n  return e instanceof Error &&\n    /^HTTP \\d{3}$/.test(e.message) &&\n    (status === undefined || e.message === `HTTP ${status}`);\n}","tryCatchPattern":"try {\n  return await api.get<T>(path, token);\n} catch (e) {\n  if (isHttpError(e, 401)) { await refreshSession(); return api.get<T>(path, token); }\n  if (isHttpError(e) && /^HTTP 5\\d{2}$/.test(e.message)) throw new RetryableError(e.message);\n  throw e;\n}","preventionTips":["Centralize all requests in the `api` wrapper so HTTP errors are handled in one place.","Surface the response body/status alongside the code for easier diagnosis (extend the thrown Error).","For 401s, implement a token refresh interceptor rather than letting users hit raw errors.","Rebuild the Expo app after changing `EXPO_PUBLIC_API_URL` (public vars are inlined at build time)."],"tags":["react-native","fetch","http","expo","network"],"backgroundTag":null,"analyzedSha":"60aaae52bb2af8162732751a4332f62a5fef518b","analyzedAt":"2026-08-13T17:32:34.717Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}