sickn33/agentic-awesome-skills · info · Error
HTTP ${posts.status}
Error message
HTTP ${posts.status} What it means
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).
Source
Thrown at skills/fp-async/SKILL.md:54
## 1. Wrapping Promises Safely
### The Problem: Try/Catch Everywhere
```typescript
// BEFORE: Try/catch hell
async function getUserData(userId: string) {
try {
const response = await fetch(`/api/users/${userId}`)
if (!response.ok) {
throw new Error(`HTTP ${response.status}`)
}
const user = await response.json()
try {
const posts = await fetch(`/api/users/${userId}/posts`)
if (!posts.ok) {
throw new Error(`HTTP ${posts.status}`)
}
const postsData = await posts.json()
return { user, posts: postsData }
} catch (postsError) {
// Now what? Return partial data? Rethrow? Log?
console.error('Failed to fetch posts:', postsError)
return { user, posts: [] }
}
} catch (error) {
// Lost all context about what failed
console.error('Something failed:', error)
throw error
}
}
```
### The Solution: Wrap Once, Handle Cleanly
View on GitHub (pinned to 58d857988f)
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
Example fix
// before
if (!posts.ok) throw new Error(`HTTP ${posts.status}`)
// after
const getPosts = (userId: string) =>
TE.tryCatch(
async () => {
const r = await fetch(`/api/users/${userId}/posts`)
if (!r.ok) throw new Error(`HTTP ${r.status}`)
return r.json()
},
E.toError
)
pipe(
getUser(userId),
TE.chain(user => pipe(
getPosts(userId),
TE.map(posts => ({ user, posts }))
))
) Defensive patterns
Strategy: try-catch
Validate before calling
const posts = await fetch(`/api/users/${userId}/posts`)
if (!posts.ok) throw new Error(`HTTP ${posts.status}: ${await posts.text()}`) Type guard
const isPostsFetchError = (e: unknown): e is Error =>
e instanceof Error && e.message.startsWith('HTTP') Try / catch
catch (postsError) {
// decide policy explicitly: rethrow, degrade to empty posts, or fail the whole operation
console.error('Failed to fetch posts:', postsError)
throw postsError
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- HTTP ${response.status}
- HTTP ${response.status}: ${response.statusText}
- Not found
- HTTP ${response.status}: ${response.statusText}
- Order not found
AI-assisted analysis of sickn33/agentic-awesome-skills@58d857988f (2026-08-26).
Data as JSON: /api/errors/e86bc3032753d704.
Report an issue: GitHub.