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

# Errors

> Error response format, error types, and how to handle them.

## Overview

The Quiz Quail API uses [RFC 9457 Problem Details](https://www.rfc-editor.org/rfc/rfc9457) for all error responses. Every error response has `Content-Type: application/problem+json` and a consistent JSON structure.

## Error Format

```json theme={null}
{
  "type": "https://quizquail.com/problems/not-found",
  "title": "Not Found",
  "status": 404,
  "detail": "Quiz 'quiz_abc123' not found"
}
```

| Field      | Type     | Description                                                  |
| ---------- | -------- | ------------------------------------------------------------ |
| `type`     | `string` | A URI identifying the error type. Stable — safe to match on. |
| `title`    | `string` | Short human-readable summary of the error type.              |
| `status`   | `number` | HTTP status code.                                            |
| `detail`   | `string` | Human-readable explanation specific to this occurrence.      |
| `instance` | `string` | *(Optional)* URI identifying this specific error occurrence. |
| `errors`   | `array`  | *(Optional)* Field-level validation errors. See below.       |

## Error Types

### 400 — Validation Error

Returned when the request body or query parameters fail validation.

```json theme={null}
{
  "type": "https://quizquail.com/problems/validation-error",
  "title": "Validation Error",
  "status": 400,
  "detail": "Request validation failed",
  "errors": [
    { "field": "title", "message": "Required", "code": "invalid_type" },
    { "field": "rounds", "message": "Array must contain at least 1 element(s)", "code": "too_small" }
  ]
}
```

The `errors` array contains per-field details:

| Field     | Type     | Description                                |
| --------- | -------- | ------------------------------------------ |
| `field`   | `string` | Dot-separated path to the invalid field.   |
| `message` | `string` | Human-readable description of the problem. |
| `code`    | `string` | *(Optional)* Machine-readable error code.  |

### 401 — Unauthorized

Returned when the request is missing or has an invalid API key.

```json theme={null}
{
  "type": "https://quizquail.com/problems/unauthorized",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Authentication required"
}
```

### 403 — Forbidden

Returned when the API key does not have the required scope for the operation.

```json theme={null}
{
  "type": "https://quizquail.com/problems/forbidden",
  "title": "Forbidden",
  "status": 403,
  "detail": "You do not have permission to perform this action"
}
```

### 404 — Not Found

Returned when the requested resource does not exist or is not accessible to your account.

```json theme={null}
{
  "type": "https://quizquail.com/problems/not-found",
  "title": "Not Found",
  "status": 404,
  "detail": "Quiz 'quiz_abc123' not found"
}
```

### 409 — Conflict

Returned when the request conflicts with the current state of a resource (e.g., trying to render a quiz that is already rendering).

```json theme={null}
{
  "type": "https://quizquail.com/problems/conflict",
  "title": "Conflict",
  "status": 409,
  "detail": "A render is already in progress for this quiz"
}
```

### 429 — Too Many Requests

Returned when you exceed the [rate limit](/rate-limiting) for the endpoint. Includes a `Retry-After` header.

```json theme={null}
{
  "type": "https://quizquail.com/problems/rate-limit-exceeded",
  "title": "Too Many Requests",
  "status": 429,
  "detail": "Rate limit exceeded. Try again in 12 seconds."
}
```

### 500 — Internal Server Error

Returned when an unexpected error occurs. The `detail` field will not contain sensitive information.

```json theme={null}
{
  "type": "https://quizquail.com/problems/internal-error",
  "title": "Internal Server Error",
  "status": 500,
  "detail": "An unexpected error occurred"
}
```

## Handling Errors

### Match on `type`, Not `title`

The `type` URI is stable and safe to use in error-handling logic. The `title` and `detail` fields are human-readable and may change.

```javascript theme={null}
async function createQuiz(apiKey, quizData) {
  const response = await fetch("https://app.quiz-quail.com/api/v1/quizzes", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${apiKey}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(quizData),
  });

  if (!response.ok) {
    const error = await response.json();

    switch (error.type) {
      case "https://quizquail.com/problems/validation-error":
        console.error("Validation failed:", error.errors);
        break;
      case "https://quizquail.com/problems/rate-limit-exceeded":
        const retryAfter = response.headers.get("Retry-After");
        console.error(`Rate limited. Retry in ${retryAfter}s`);
        break;
      case "https://quizquail.com/problems/unauthorized":
        console.error("Check your API key");
        break;
      default:
        console.error(`API error: ${error.detail}`);
    }

    throw new Error(error.detail);
  }

  return response.json();
}
```

### Display Validation Errors

For 400 responses with field-level errors, map them to your form fields:

```javascript theme={null}
if (error.type === "https://quizquail.com/problems/validation-error") {
  for (const fieldError of error.errors) {
    console.error(`${fieldError.field}: ${fieldError.message}`);
    // e.g. "title: Required"
    // e.g. "rounds: Array must contain at least 1 element(s)"
  }
}
```
