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

# Errors

> The shared tRPC error envelope and common status codes

Every Nudj API endpoint uses the same error envelope — the tRPC wire format — so clients can share a single error handler across all three APIs.

## Envelope

```json theme={null}
{
  "message": "Human-readable description of the problem.",
  "code": "TRPC_ERROR_CODE",
  "data": {
    "httpStatus": 400,
    "path": "endpointName"
  }
}
```

| Field             | Description                                                                                                                           |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `message`         | Free-text description. Safe to surface in developer-facing logs. **Don't surface verbatim to end users** — messages aren't localised. |
| `code`            | Stable tRPC error code — e.g. `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `BAD_REQUEST`, `INTERNAL_SERVER_ERROR`.                       |
| `data.httpStatus` | The HTTP status code of the response. Mirrors the response status line.                                                               |
| `data.path`       | Name of the endpoint procedure that rejected the request. Useful for log correlation.                                                 |

## Common status codes

### 400 — `BAD_REQUEST`

Zod validation failure on the request body, a path/query parameter, or a required header.

```json theme={null}
{
  "message": "Invalid input: expected string for body.communityId",
  "code": "BAD_REQUEST",
  "data": { "httpStatus": 400, "path": "createChallenge" }
}
```

**Fix:** read the `message` — it names the offending field. Re-check your request shape against the endpoint's schema.

### 401 — `UNAUTHORIZED`

Token missing, malformed, expired, or issued for a different organisation's subdomain.

```json theme={null}
{
  "message": "You must be logged in to access this endpoint.",
  "code": "UNAUTHORIZED",
  "data": { "httpStatus": 401, "path": "getAchievements" }
}
```

**Fix:** see [Authentication troubleshooting](/api-reference/authentication#troubleshooting).

### 403 — `FORBIDDEN`

Token is valid but lacks the RBAC permissions for the requested action. Most common on Admin endpoints.

```json theme={null}
{
  "message": "Insufficient permissions for this operation.",
  "code": "FORBIDDEN",
  "data": { "httpStatus": 403, "path": "deleteChallenge" }
}
```

**Fix:** raise the token's role (if appropriate), or narrow the request to a community the token has access to.

### 404 — `NOT_FOUND`

No entity matches the request — wrong id, wrong organisation, or the entity has been deleted.

```json theme={null}
{
  "message": "Challenge not found.",
  "code": "NOT_FOUND",
  "data": { "httpStatus": 404, "path": "getChallengeById" }
}
```

### 409 — `CONFLICT`

The request would break a uniqueness constraint or violates current state (e.g. trying to claim a reward already claimed, re-use a one-time code, or register a duplicate domain).

```json theme={null}
{
  "message": "Reward already claimed.",
  "code": "CONFLICT",
  "data": { "httpStatus": 409, "path": "claimReward" }
}
```

### 500 — `INTERNAL_SERVER_ERROR`

An unexpected runtime error. The response body surfaces the tRPC `code` but the detailed stack trace is logged server-side and not returned.

```json theme={null}
{
  "message": "An unexpected error occurred.",
  "code": "INTERNAL_SERVER_ERROR",
  "data": { "httpStatus": 500, "path": "getMyProfile" }
}
```

<Note>
  **Response-validation errors can also surface as 500.** If the API returns data that fails its own output schema (historically this has happened on a few integration endpoints when new enum values are rolled out), clients receive a generic 500 rather than a typed failure. If you consistently hit a 500 on a previously-working endpoint, file a bug — it's very likely a schema regression rather than a runtime fault.
</Note>

## Client handling pattern

<CodeGroup>
  ```javascript JavaScript theme={null}
  async function nudjRequest(path, init = {}) {
    const response = await fetch(`https://${DOMAIN}${path}`, {
      ...init,
      headers: {
        'Authorization': `Bearer ${process.env.NUDJ_API_TOKEN}`,
        'Content-Type': 'application/json',
        ...(init.headers ?? {}),
      },
    });

    if (!response.ok) {
      const error = await response.json().catch(() => null);
      // error has shape { message, code, data: { httpStatus, path } }
      throw Object.assign(
        new Error(error?.message ?? `HTTP ${response.status}`),
        { code: error?.code, httpStatus: response.status, path: error?.data?.path },
      );
    }

    return response.json();
  }
  ```

  ```python Python theme={null}
  import os, requests

  def nudj_request(path, **kwargs):
      response = requests.request(
          url=f"https://{DOMAIN}{path}",
          headers={
              "Authorization": f"Bearer {os.environ['NUDJ_API_TOKEN']}",
              "Content-Type": "application/json",
              **kwargs.pop("headers", {}),
          },
          **kwargs,
      )
      if not response.ok:
          try:
              error = response.json()
          except ValueError:
              error = {}
          raise RuntimeError(
              f"Nudj API error: {error.get('code', response.status_code)} — {error.get('message')}"
          )
      return response.json()
  ```
</CodeGroup>

## Retries

The APIs don't emit `Retry-After` headers, but 5xx responses (`500`, `502`, `503`) are always safe to retry with exponential backoff. Do **not** blindly retry `4xx` — those are request-shape errors and will fail identically a second time.

## Related

<CardGroup cols={2}>
  <Card title="Authentication" icon="key" href="/api-reference/authentication">
    401 and 403 scenarios in depth.
  </Card>

  <Card title="Pagination" icon="list-ol" href="/api-reference/pagination">
    Common 400 errors on list endpoints.
  </Card>
</CardGroup>
