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

# Error Handling

> Error codes, response formats, and retry strategies for the PixelByte API

# Error Handling

All API errors follow a consistent format, making it easy to handle failures programmatically.

## Error Response Format

Every error response includes:

```json theme={null}
{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable description of what went wrong",
    "details": {}
  },
  "requestId": "req_abc123"
}
```

| Field           | Type      | Description                     |
| --------------- | --------- | ------------------------------- |
| `success`       | `boolean` | Always `false` for errors       |
| `error.code`    | `string`  | Machine-readable error code     |
| `error.message` | `string`  | Human-readable description      |
| `error.details` | `object`  | Additional context (optional)   |
| `requestId`     | `string`  | Unique request ID for debugging |

<Tip>
  Always include the `requestId` when contacting support — it helps us trace the exact request in our logs.
</Tip>

## Job Error Codes

These errors occur when working with jobs, models, and user operations:

| Code                       | HTTP Status | Description                                          |
| -------------------------- | ----------- | ---------------------------------------------------- |
| `MODEL_NOT_FOUND`          | 404         | The specified model slug does not exist              |
| `MODEL_VERSION_NOT_FOUND`  | 404         | The specified model version does not exist           |
| `MODEL_VERSION_NOT_ACTIVE` | 400         | The model version exists but is not currently active |
| `INVALID_MODEL_SLUG`       | 400         | The model slug format is invalid                     |
| `MODEL_ACCESS_DENIED`      | 403         | You do not have access to this model                 |
| `INSUFFICIENT_BALANCE`     | 402         | Your account balance is too low for this job         |
| `INVALID_INPUT`            | 400         | The input parameters are invalid or missing          |
| `JOB_NOT_FOUND`            | 404         | The specified job ID does not exist                  |
| `JOB_CANNOT_BE_CANCELLED`  | 400         | The job is in a state that cannot be cancelled       |
| `CONCURRENT_JOBS_EXCEEDED` | 429         | Too many jobs running simultaneously                 |
| `INVALID_WEBHOOK_URL`      | 400         | The provided webhook URL is not valid                |
| `USER_NOT_FOUND`           | 404         | The user account was not found                       |
| `USER_BANNED`              | 403         | The user account has been banned                     |
| `USER_INACTIVE`            | 403         | The user account is inactive                         |
| `UNAUTHORIZED_ACCESS`      | 403         | You are not authorized to perform this action        |
| `PUBSUB_PUBLISH_FAILED`    | 503         | Internal message queue failure                       |
| `INTERNAL_ERROR`           | 500         | An unexpected server error occurred                  |

## Authentication Error Codes

These errors occur during authentication and authorization:

| Code                    | HTTP Status | Description                                             |
| ----------------------- | ----------- | ------------------------------------------------------- |
| `UNAUTHORIZED`          | 401         | No authentication credentials provided                  |
| `INVALID_API_KEY`       | 401         | The API key is not valid                                |
| `API_KEY_INACTIVE`      | 401         | The API key has been deactivated                        |
| `API_KEY_EXPIRED`       | 401         | The API key has expired                                 |
| `IP_NOT_WHITELISTED`    | 403         | Request IP is not in the allowed list                   |
| `OPERATION_NOT_ALLOWED` | 403         | The API key does not have permission for this operation |
| `INVALID_WORKER_SECRET` | 401         | Invalid worker authentication secret                    |
| `INVALID_SESSION`       | 401         | The session token is invalid or expired                 |
| `FORBIDDEN`             | 403         | Access denied                                           |
| `CONFIG_ERROR`          | 500         | Server configuration error                              |

## Retry Strategy

Not all errors should be retried. Use the HTTP status code to determine the right approach:

### Retryable Errors

<Steps>
  <Step title="503 Service Unavailable">
    Temporary server issue. Retry with **exponential backoff**:

    ```
    wait = min(baseDelay × 2^attempt, maxDelay)
    ```

    Start with 1 second, max 60 seconds, up to 5 attempts.
  </Step>

  <Step title="429 Too Many Requests">
    Rate limit hit. Check the `retryAfter` field in the response and wait the specified duration before retrying.

    ```json theme={null}
    {
      "error": {
        "code": "CONCURRENT_JOBS_EXCEEDED",
        "message": "Too many concurrent jobs",
        "details": { "retryAfter": 30 }
      }
    }
    ```
  </Step>

  <Step title="500 Internal Server Error">
    Unexpected server error. Retry with **exponential backoff**, but limit attempts. If it persists, contact support.
  </Step>
</Steps>

### Non-Retryable Errors

| Status                   | Action                                    |
| ------------------------ | ----------------------------------------- |
| **400** Bad Request      | Fix the request parameters and resubmit   |
| **401** Unauthorized     | Check your API key and authentication     |
| **402** Payment Required | Add funds to your account                 |
| **403** Forbidden        | Verify your permissions and access rights |
| **404** Not Found        | Check the resource ID or model slug       |

## Implementation Examples

<CodeGroup>
  ```javascript Node.js theme={null}
  async function submitWithRetry(jobData, maxRetries = 5) {
    for (let attempt = 0; attempt < maxRetries; attempt++) {
      const response = await fetch("https://api.muvi.video/v1/jobs/submit", {
        method: "POST",
        headers: {
          "Authorization": "Bearer YOUR_API_KEY",
          "Content-Type": "application/json"
        },
        body: JSON.stringify(jobData)
      });

      if (response.ok) {
        return await response.json();
      }

      const error = await response.json();

      // Non-retryable errors
      if (response.status >= 400 && response.status < 500 && response.status !== 429) {
        throw new Error(`${error.error.code}: ${error.error.message}`);
      }

      // Rate limited — respect retryAfter
      if (response.status === 429) {
        const retryAfter = error.error.details?.retryAfter || 30;
        await new Promise(r => setTimeout(r, retryAfter * 1000));
        continue;
      }

      // Server error — exponential backoff
      const delay = Math.min(1000 * Math.pow(2, attempt), 60000);
      await new Promise(r => setTimeout(r, delay));
    }

    throw new Error("Max retries exceeded");
  }
  ```

  ```python Python theme={null}
  import time
  import requests

  def submit_with_retry(job_data, max_retries=5):
      for attempt in range(max_retries):
          response = requests.post(
              "https://api.muvi.video/v1/jobs/submit",
              headers={
                  "Authorization": "Bearer YOUR_API_KEY",
                  "Content-Type": "application/json"
              },
              json=job_data
          )

          if response.ok:
              return response.json()

          error = response.json()

          # Non-retryable errors
          if 400 <= response.status_code < 500 and response.status_code != 429:
              raise Exception(f"{error['error']['code']}: {error['error']['message']}")

          # Rate limited — respect retryAfter
          if response.status_code == 429:
              retry_after = error["error"].get("details", {}).get("retryAfter", 30)
              time.sleep(retry_after)
              continue

          # Server error — exponential backoff
          delay = min(2 ** attempt, 60)
          time.sleep(delay)

      raise Exception("Max retries exceeded")
  ```
</CodeGroup>

## Best Practices

<CardGroup cols={2}>
  <Card title="Log Request IDs" icon="list">
    Store the `requestId` from every response for debugging and support requests.
  </Card>

  <Card title="Handle Gracefully" icon="shield">
    Always check `success` field before accessing `data`. Never assume a request succeeded.
  </Card>

  <Card title="Use Estimate First" icon="calculator">
    Call the estimate endpoint before submitting to catch `INSUFFICIENT_BALANCE` early.
  </Card>

  <Card title="Monitor Error Rates" icon="chart-line">
    Track error codes in your application to identify patterns and potential issues.
  </Card>
</CardGroup>
