{"record":{"id":"e86bc3032753d704","repo":"sickn33/agentic-awesome-skills","slug":"http-posts-status","errorCode":null,"errorMessage":"HTTP ${posts.status}","messagePattern":"HTTP \\$\\{posts\\.status\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"info","filePath":"skills/fp-async/SKILL.md","lineNumber":54,"sourceCode":"\n## 1. Wrapping Promises Safely\n\n### The Problem: Try/Catch Everywhere\n\n```typescript\n// BEFORE: Try/catch hell\nasync function getUserData(userId: string) {\n  try {\n    const response = await fetch(`/api/users/${userId}`)\n    if (!response.ok) {\n      throw new Error(`HTTP ${response.status}`)\n    }\n    const user = await response.json()\n\n    try {\n      const posts = await fetch(`/api/users/${userId}/posts`)\n      if (!posts.ok) {\n        throw new Error(`HTTP ${posts.status}`)\n      }\n      const postsData = await posts.json()\n      return { user, posts: postsData }\n    } catch (postsError) {\n      // Now what? Return partial data? Rethrow? Log?\n      console.error('Failed to fetch posts:', postsError)\n      return { user, posts: [] }\n    }\n  } catch (error) {\n    // Lost all context about what failed\n    console.error('Something failed:', error)\n    throw error\n  }\n}\n```\n\n### The Solution: Wrap Once, Handle Cleanly\n","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/sickn33/agentic-awesome-skills/blob/58d857988fcfac6986206bca2b2fe223aa437e4b/skills/fp-async/SKILL.md#L36-L72","documentation":"Illustrative throw from the nested posts-fetch example in skills/fp-async/SKILL.md's BEFORE snippet. When the second request (GET /api/users/:userId/posts) returns a non-2xx status, the inner !posts.ok guard throws, and the surrounding code is left with the ambiguous partial-state question the skill highlights (return partial data, rethrow, or log).","triggerScenarios":"The user fetch succeeded but the posts endpoint returns 404/500/403; e.g. posts route not deployed, user has no posts endpoint, or rate limiting kicking in only on the second call.","commonSituations":"Copying the anti-pattern snippet verbatim; partial API availability where one resource exists and a related one does not; gateway timeouts on the heavier posts query.","solutions":["Log posts.status and the response body to identify why the posts endpoint failed","Verify the posts route exists and the user ID is valid for it","Refactor to the skill's AFTER pattern using TE.tryCatch per request and TE.chain to sequence them, so failure of posts is a Left value instead of an ambiguous throw","Decide explicitly whether posts are optional (use TE.alt / orElse to return an empty list) or required"],"exampleFix":"// before\nif (!posts.ok) throw new Error(`HTTP ${posts.status}`)\n// after\nconst getPosts = (userId: string) =>\n  TE.tryCatch(\n    async () => {\n      const r = await fetch(`/api/users/${userId}/posts`)\n      if (!r.ok) throw new Error(`HTTP ${r.status}`)\n      return r.json()\n    },\n    E.toError\n  )\n\npipe(\n  getUser(userId),\n  TE.chain(user => pipe(\n    getPosts(userId),\n    TE.map(posts => ({ user, posts }))\n  ))\n)","handlingStrategy":"try-catch","validationCode":"const posts = await fetch(`/api/users/${userId}/posts`)\nif (!posts.ok) throw new Error(`HTTP ${posts.status}: ${await posts.text()}`)","typeGuard":"const isPostsFetchError = (e: unknown): e is Error =>\n  e instanceof Error && e.message.startsWith('HTTP')","tryCatchPattern":"catch (postsError) {\n  // decide policy explicitly: rethrow, degrade to empty posts, or fail the whole operation\n  console.error('Failed to fetch posts:', postsError)\n  throw postsError\n}","preventionTips":["Decide upfront whether secondary resources are required or optional","Use a single fetch wrapper for all calls so the error shape is uniform","Never swallow a secondary failure silently; make the partial-data policy explicit"],"tags":["documentation","fetch","http-status","nested-try-catch"],"backgroundTag":"fetch-non-2xx-response","analyzedSha":"58d857988fcfac6986206bca2b2fe223aa437e4b","analyzedAt":"2026-08-26T11:55:59.350Z","schemaVersion":2},"datasetVersion":"2026-08-26T14:46:13.012Z"}