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

  1. Log posts.status and the response body to identify why the posts endpoint failed
  2. Verify the posts route exists and the user ID is valid for it
  3. 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
  4. 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

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


AI-assisted analysis of sickn33/agentic-awesome-skills@58d857988f (2026-08-26). Data as JSON: /api/errors/e86bc3032753d704. Report an issue: GitHub.