{"record":{"id":"c490fd511a07c717","repo":"sickn33/agentic-awesome-skills","slug":"http-res-status","errorCode":null,"errorMessage":"HTTP ${res.status}","messagePattern":"HTTP \\$\\{res\\.status\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"skills/fp-react/SKILL.md","lineNumber":254,"sourceCode":"\n## 3. Data Fetching with TaskEither\n\nTaskEither = async operation that might fail. Perfect for API calls.\n\n### Basic Fetch Hook\n\n```typescript\nimport { useState, useEffect } from 'react'\nimport * as TE from 'fp-ts/TaskEither'\nimport * as E from 'fp-ts/Either'\nimport { pipe } from 'fp-ts/function'\n\n// Wrap fetch in TaskEither\nconst fetchJson = <T>(url: string): TE.TaskEither<Error, T> =>\n  TE.tryCatch(\n    async () => {\n      const res = await fetch(url)\n      if (!res.ok) throw new Error(`HTTP ${res.status}`)\n      return res.json()\n    },\n    (err) => err instanceof Error ? err : new Error(String(err))\n  )\n\n// Custom hook\nfunction useFetch<T>(url: string) {\n  const [data, setData] = useState<T | null>(null)\n  const [error, setError] = useState<Error | null>(null)\n  const [loading, setLoading] = useState(true)\n\n  useEffect(() => {\n    setLoading(true)\n    setError(null)\n\n    pipe(\n      fetchJson<T>(url),\n      TE.match(","sourceCodeStart":236,"sourceCodeEnd":272,"githubUrl":"https://github.com/sickn33/agentic-awesome-skills/blob/58d857988fcfac6986206bca2b2fe223aa437e4b/skills/fp-react/SKILL.md#L236-L272","documentation":"In the fp-react skill's fetch wrapper, this error is thrown when the HTTP response's `ok` flag is false — any 4xx or 5xx status. The throw happens inside TE.tryCatch's attempt function, so it is automatically captured as the Left of the TaskEither rather than escaping to the caller.","triggerScenarios":"Any fetch(url) returning a non-2xx status: 401 with an expired token, 404 on a mistyped endpoint, 500 from the server, or a 429 rate limit.","commonSituations":"Missing or expired auth headers in the fetch call; proxy/dev-server rewriting API paths; backend deployed with breaking route changes; CORS preflight failures surfacing as opaque errors.","solutions":["Inspect res.status (and the response body) before assuming a client bug — the number identifies the class of failure","Ensure auth tokens and correct base URL/headers are attached to fetch","Handle the Left branch of the TaskEither to show user-facing messages per status code"],"exampleFix":"// before\nconst res = await fetch(url)\nif (!res.ok) throw new Error(`HTTP ${res.status}`)\n\n// after: enrich the Left with status and body\nconst fetchJson = <T>(url: string): TE.TaskEither<Error, T> =>\n  TE.tryCatch(\n    async () => {\n      const res = await fetch(url)\n      if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`)\n      return res.json()\n    },\n    (err) => err instanceof Error ? err : new Error(String(err))\n  )","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"const isOkStatus = (s: number) => s >= 200 && s < 300","tryCatchPattern":"// with TaskEither the throw never escapes; handle the Left:\npipe(\n  fetchJson<T>(url),\n  TE.mapLeft((e) => e.message /* e.g. 'HTTP 401' */)\n)","preventionTips":["Attach auth headers and correct base URL to every fetch","Map status codes to user-facing messages centrally instead of letting raw Errors bubble"],"tags":["http","fetch","react","fp-ts","network"],"backgroundTag":"http-error-status","analyzedSha":"58d857988fcfac6986206bca2b2fe223aa437e4b","analyzedAt":"2026-08-26T11:55:59.350Z","schemaVersion":2},"datasetVersion":"2026-08-26T14:46:13.012Z"}