# Error Handling

> ZopNight uses standard HTTP status codes and returns structured JSON error responses. Reference for status codes, retry strategy, and common error scenarios.

Source: https://zop.dev/developer-docs/reference/errors

---

ZopNight uses standard HTTP status codes and returns structured error responses.

## Error Response Format

When an error occurs, the API returns a JSON response with a string `error` field:

```json
{
  "error": "schedule not found"
}
```

**Info**

The wrapper field is always `error`, but the message text is produced by the
backend service that handled the request, so wording can vary slightly between
services (resources, schedules, deployments, etc.).

## HTTP Status Codes

<table>
  <thead>
    <tr>
      <th>Code</th>
      <th>Meaning</th>
      <th>Common Causes</th>
    </tr>
  </thead>
  <tbody>
    {[
      ['200', 'OK', 'Request succeeded'],
      ['201', 'Created', 'Resource created successfully'],
      ['400', 'Bad Request', 'Invalid request body, missing required fields, validation errors'],
      ['401', 'Unauthorized', 'Missing, invalid, or expired JWT token'],
      ['403', 'Forbidden', "Insufficient permissions or accessing another organization's resources"],
      ['404', 'Not Found', 'Resource, schedule, or endpoint does not exist'],
      ['409', 'Conflict', 'Duplicate resource, schedule name already exists'],
      ['429', 'Too Many Requests', 'Rate limit exceeded — see Rate Limiting'],
      ['500', 'Internal Server Error', 'Unexpected server error — retry with backoff'],
      ['502', 'Bad Gateway', 'Gateway could not reach the backend service'],
      ['503', 'Service Unavailable', 'Service is temporarily unavailable'],
    ].map(([code, meaning, causes]) => (
      <tr key={code}>
        <td>
          <code className={`px-1.5 py-0.5 rounded text-xs font-bold ${
            code.startsWith('2') ? 'bg-emerald-100 text-emerald-700' :
            code.startsWith('4') ? 'bg-amber-100 text-amber-700' :
            'bg-red-100 text-red-700'
          }`}>{code}</code>
        </td>
        <td><strong>{meaning}</strong></td>
        <td>{causes}</td>
      </tr>
    ))}
  </tbody>
</table>

## Retry Strategy

| Status | Action |
|---|---|
| **4xx (except 429)** | Do not retry. Fix the request and try again. |
| **429** | Back off and retry after the period indicated in the response. |
| **500** | Retry with exponential backoff (1s, 2s, 4s, 8s). |
| **502/503** | Retry with exponential backoff. The service may be restarting. |

**Tip**

POST endpoints for actions (start/stop) are idempotent. It is safe to retry these
operations without risk of double-execution.

## Common Error Scenarios

### Expired Token

```json
HTTP 401
{
  "error": "token has expired"
}
```

Use the `/auth/refresh` endpoint to get a new access token.

### Invalid Request Body

```json
HTTP 400
{
  "error": "invalid cron expression: '0 25 * * *'"
}
```

### Resource Not Found

```json
HTTP 404
{
  "error": "resource not found"
}
```

### Rate Limited

```json
HTTP 429
{
  "error": "rate limit exceeded"
}
```
