> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vibefollow.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> Global error envelope, status-code reference, and recovery patterns.

Vibefollow uses a single error envelope across the API. Every non-2xx response carries a body of this shape:

```json theme={null}
{
  "errors": [
    {
      "code": "validation_failed",
      "message": "required",
      "field": "external_user_id"
    }
  ]
}
```

`code` is machine-readable. `message` is human-readable. `field` names the offending field when the server can pinpoint one.

## Status reference

| Status | Class of problem                                      | Retryable  | Typed SDK error         |
| ------ | ----------------------------------------------------- | ---------- | ----------------------- |
| `400`  | Malformed request (not JSON, etc.)                    | No         | `VibeFollowError`       |
| `401`  | API key missing or malformed                          | No         | `AuthError`             |
| `403`  | API key valid but not authorised                      | No         | `AuthError`             |
| `404`  | Resource not found                                    | No         | `VibeFollowError`       |
| `409`  | Conflict (idempotency key reused with different body) | No         | `ValidationError`       |
| `422`  | Validation failed                                     | No         | `ValidationError`       |
| `429`  | Rate limited                                          | Yes (auto) | `RateLimitError`        |
| `5xx`  | Server error                                          | Yes (auto) | `ServerError`           |
| `0`    | Network failure (DNS, refused, timeout)               | Yes (auto) | `NetworkError`          |
| `0`    | Webhook signature mismatch                            | No         | `WebhookSignatureError` |

<Tip>
  The SDK [error classes](/sdk/errors) wrap these — you write `catch (err) { if (err instanceof AuthError) ... }` instead of inspecting status codes by hand.
</Tip>

## Error class reference

<AccordionGroup>
  <Accordion title="AuthError — 401 / 403" icon="key">
    API key missing, malformed, or revoked. Not retryable.

    ```ts theme={null}
    import { AuthError } from '@vibefollow/sdk';

    try {
      await vf.users.signedUp('usr_42');
    } catch (err) {
      if (err instanceof AuthError) {
        // Configuration issue &mdash; key is missing or revoked. Alert.
        alerts.fire('vibefollow_auth', err.message);
      } else {
        throw err;
      }
    }
    ```
  </Accordion>

  <Accordion title="ValidationError — 422 / 409" icon="circle-exclamation">
    Request body failed schema validation, or the `Idempotency-Key` was reused with a different body. `.field` names the offending field when available.

    ```ts theme={null}
    import { ValidationError } from '@vibefollow/sdk';

    try {
      await vf.events.track('feature_used', 'usr_42', {});
    } catch (err) {
      if (err instanceof ValidationError) {
        // Bug in our event-emitting code. Log with the field and fix.
        logger.error({ field: err.field, message: err.message }, 'vibefollow validation');
      } else {
        throw err;
      }
    }
    ```
  </Accordion>

  <Accordion title="RateLimitError — 429" icon="gauge-high">
    Rate limit hit. The SDK auto-retries up to `maxRetries`; surfaced when retries are exhausted. `.retryAfterMs` carries the server's `Retry-After`.

    ```ts theme={null}
    import { RateLimitError } from '@vibefollow/sdk';

    try {
      await vf.users.signedUp('usr_42');
    } catch (err) {
      if (err instanceof RateLimitError) {
        // Already retried internally. Back off harder on our end.
        await sleep(err.retryAfterMs);
      } else {
        throw err;
      }
    }
    ```
  </Accordion>

  <Accordion title="ServerError — 5xx" icon="server">
    Server-side problem on Vibefollow's end. Retryable by definition; surfaced when retries are exhausted.

    ```ts theme={null}
    import { ServerError } from '@vibefollow/sdk';

    try {
      await vf.users.signedUp('usr_42');
    } catch (err) {
      if (err instanceof ServerError) {
        // Transient. Already retried; surface and continue.
        logger.warn({ status: err.status }, 'vibefollow server error');
      } else {
        throw err;
      }
    }
    ```
  </Accordion>

  <Accordion title="NetworkError — status 0" icon="signal">
    DNS, refused, abort, timeout. Retryable. The SDK auto-retries; surfaced when the retry budget is exhausted.

    ```ts theme={null}
    import { NetworkError } from '@vibefollow/sdk';

    try {
      await vf.users.signedUp('usr_42');
    } catch (err) {
      if (err instanceof NetworkError) {
        logger.warn({ cause: err.cause }, 'vibefollow network error');
      } else {
        throw err;
      }
    }
    ```
  </Accordion>

  <Accordion title="WebhookSignatureError — status 0" icon="shield-halved">
    HMAC mismatch or timestamp outside the ±5 minute window. Not retryable. Return `401` to Vibefollow.

    ```ts theme={null}
    import { WebhookSignatureError } from '@vibefollow/sdk';

    try {
      const event = vf.webhooks.constructEvent(rawBody, sigHeader, secret);
      // ...
    } catch (err) {
      if (err instanceof WebhookSignatureError) {
        return res.sendStatus(401);
      }
      throw err;
    }
    ```
  </Accordion>

  <Accordion title="VibeFollowError — base class" icon="triangle-exclamation">
    Catch this to handle every SDK error uniformly.

    ```ts theme={null}
    import { VibeFollowError } from '@vibefollow/sdk';

    try {
      await vf.users.signedUp('usr_42');
    } catch (err) {
      if (err instanceof VibeFollowError) {
        logger.error({ status: err.status, code: err.code, message: err.message }, 'vibefollow error');
      }
      throw err;
    }
    ```
  </Accordion>
</AccordionGroup>

## Common codes

| Code                        | Status | Meaning                                                      |
| --------------------------- | ------ | ------------------------------------------------------------ |
| `auth_required`             | 401    | `Authorization` header missing or malformed                  |
| `auth_invalid`              | 401    | API key not recognised (revoked or never existed)            |
| `forbidden`                 | 403    | API key valid but not authorised for this project / resource |
| `validation_failed`         | 422    | Request body failed Zod schema validation                    |
| `idempotency_key_reuse`     | 422    | Same `Idempotency-Key`, different body                       |
| `rate_limited`              | 429    | Rate limit hit; respect `Retry-After`                        |
| `server_error`              | 5xx    | Vibefollow had a problem                                     |
| `webhook_signature_invalid` | (n/a)  | SDK-thrown; HMAC mismatch or timestamp out of tolerance      |

## When a request silently disappears

<Info>
  Vibefollow returns `202 Accepted` for ingest endpoints — that confirms enqueue, not processing. If you `track()` an event but don't see it in the dashboard, work through this checklist.
</Info>

<Steps>
  <Step title="Check the events log">
    Look in **Settings → Developers → Recent events**. Malformed events land in the quarantine view, not the main stream.
  </Step>

  <Step title="Check for near-miss event names">
    Confirm your event name doesn't collide with a near-miss canonical name (e.g. `user_signedUp` vs `user_signed_up`). The dashboard flags near-misses.
  </Step>

  <Step title="Confirm the user ID matches">
    Confirm the `external_user_id` matches what `identify` used. Mismatched IDs create orphaned events that are still ingested but not linked.
  </Step>
</Steps>

## See also

<CardGroup cols={3}>
  <Card title="SDK errors" icon="cube" href="/sdk/errors">
    Full typed error hierarchy with recovery patterns.
  </Card>

  <Card title="API authentication" icon="key" href="/api-reference/authentication">
    Wire-level auth failure modes.
  </Card>

  <Card title="Webhook signatures" icon="shield-halved" href="/webhooks/signature-verification">
    The `WebhookSignatureError` case.
  </Card>
</CardGroup>
