sickn33/agentic-awesome-skills · info · Error

HTTP ${response.status}

Error message

HTTP ${response.status}

What it means

This is not a runtime error from a library; it is an illustrative throw inside the BEFORE (anti-pattern) example in skills/fp-async/SKILL.md. It shows the common pattern of manually checking response.ok after fetch() and throwing a generic Error with only the HTTP status code. The skill uses it to motivate replacing ad-hoc try/catch nesting with fp-ts TaskEither.

Source

Thrown at skills/fp-async/SKILL.md:47

```typescript
// TaskEither<Error, User> means:
// "An async operation that either fails with Error or succeeds with User"
```

---

## 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)

View on GitHub (pinned to 58d857988f)

Solutions

  1. Inspect response.status (log the body too) to find the real HTTP problem before blaming the code
  2. Check the URL, method, and headers of the fetch call against the API contract
  3. Use the skill's AFTER pattern: wrap fetch in TE.tryCatch so errors flow through TaskEither instead of nested throws
  4. Distinguish retryable statuses (5xx, 429) from permanent ones (4xx) in your handler

Example fix

// before
if (!response.ok) throw new Error(`HTTP ${response.status}`)
// after
const fetchJson = <T>(url: string): TE.TaskEither<Error, T> =>
  TE.tryCatch(
    async () => {
      const r = await fetch(url)
      if (!r.ok) throw new Error(`HTTP ${r.status}: ${r.statusText}`)
      return r.json()
    },
    E.toError
  )
Defensive patterns

Strategy: try-catch

Validate before calling

const res = await fetch(url)
if (!res.ok) {
  const body = await res.text().catch(() => '')
  throw new Error(`HTTP ${res.status} ${res.statusText}: ${body.slice(0, 200)}`)
}

Type guard

const isHttpError = (e: unknown): e is Error & { status?: number } =>
  e instanceof Error && /HTTP \d{3}/.test(e.message)

Try / catch

try {
  const user = await fetchJson(url)
} catch (e) {
  if (e instanceof TypeError) { /* network failure */ }
  else { /* HTTP status failure: parse status from message */ }
}

Prevention

When it happens

Trigger: Running the sample code against an endpoint that returns any non-2xx status (404, 500, 401) for GET /api/users/:userId; fetch() itself does not throw on HTTP error statuses, so the manual !response.ok check is the only thing that raises this.

Common situations: Developers copying the skill's BEFORE snippet into real code; API base URL misconfigured so every request 404s; missing auth header producing 401; server down returning 5xx; proxy returning 502.

Related errors


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