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

# Error Handling

> Understand and handle API errors

All errors follow a consistent JSON structure:

```json theme={null}
{
  "error": {
    "code": "SCHEME_NOT_FOUND",
    "message": "No scheme 'ordinario' found for country 'BR' year 2025",
    "details": {},
    "request_id": "01HX9B2KM3..."
  }
}
```

<Tip>
  Use the `code` field for programmatic error handling, not the HTTP status code.
  Include the `request_id` when contacting support to help trace your request.
</Tip>

## Error codes

| Code                        | HTTP | When                                                              |
| --------------------------- | ---- | ----------------------------------------------------------------- |
| `SCHEME_NOT_FOUND`          | 404  | Country/scheme/year combination not loaded                        |
| `VALIDATION_ERROR`          | 422  | Request body failed validation                                    |
| `FORMULA_EVALUATION_ERROR`  | 422  | Runtime formula failure (e.g., division by zero)                  |
| `CIRCULAR_DEPENDENCY`       | 422  | Cycle detected in concept dependencies                            |
| `REFERENCE_VALUE_NOT_FOUND` | 422  | Reference value (UMA, SMMLV) not available for the requested date |
| `MISSING_AUTH_TOKEN`        | 401  | No `Authorization` header provided                                |
| `INVALID_API_KEY`           | 401  | The API key is not valid                                          |
| `RATE_LIMIT_EXCEEDED`       | 429  | Too many requests (default: 120/min per key)                      |
| `INTERNAL_ERROR`            | 500  | Unexpected server error                                           |

## Handling errors in code

<CodeGroup>
  ```python Python theme={null}
  import httpx

  response = httpx.post(
      "https://api.clevis.dev/v1/payroll/calculate",
      headers={"Authorization": "Bearer YOUR_API_KEY"},
      json=payload,
  )

  if response.status_code != 200:
      error = response.json()["error"]
      match error["code"]:
          case "SCHEME_NOT_FOUND":
              print(f"Scheme not available: {error['message']}")
          case "VALIDATION_ERROR":
              print(f"Invalid request: {error['details']}")
          case "RATE_LIMIT_EXCEEDED":
              print("Rate limited, retry after a moment")
          case _:
              print(f"Error {error['code']}: {error['message']}")
              print(f"Request ID: {error['request_id']}")
  ```

  ```javascript Node.js theme={null}
  const response = await fetch("https://api.clevis.dev/v1/payroll/calculate", {
    method: "POST",
    headers: {
      Authorization: "Bearer YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify(payload),
  });

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

    switch (error.code) {
      case "SCHEME_NOT_FOUND":
        console.log(`Scheme not available: ${error.message}`);
        break;
      case "VALIDATION_ERROR":
        console.log("Invalid request:", error.details);
        break;
      case "RATE_LIMIT_EXCEEDED":
        console.log("Rate limited, retry after a moment");
        break;
      default:
        console.log(`Error ${error.code}: ${error.message}`);
        console.log(`Request ID: ${error.request_id}`);
    }
  }
  ```
</CodeGroup>

## Batch error handling

In batch requests, individual employee errors do not abort the entire batch (unless `fail_fast: true`). Each employee result includes its own status:

```json theme={null}
{
  "results": [
    {
      "employee_id": "emp_001",
      "status": "success",
      "calculation": { ... }
    },
    {
      "employee_id": "emp_002",
      "status": "error",
      "error": {
        "code": "FORMULA_EVALUATION_ERROR",
        "message": "Division by zero in concept 'isr'"
      }
    }
  ],
  "total_succeeded": 1,
  "total_failed": 1
}
```

Set `"fail_fast": true` in batch options to abort on the first error instead.
