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

# Errors

> Understand error responses and how to handle them

When the API encounters an error, it returns a consistent JSON error response with details about what went wrong.

## Error Response Format

All error responses follow this structure:

```json theme={null}
{
  "error": {
    "code": "error_code",
    "message": "Human-readable description of the error",
    "type": "error_type"
  }
}
```

<ResponseField name="code" type="string">
  A machine-readable error code for programmatic handling
</ResponseField>

<ResponseField name="message" type="string">
  A human-readable description of what went wrong
</ResponseField>

<ResponseField name="type" type="string">
  The category of error (validation\_error, authentication\_error, etc.)
</ResponseField>

***

## Error Types

### Validation Errors (400)

Returned when the request contains invalid data.

```json theme={null}
{
  "error": {
    "code": "invalid_format",
    "message": "externalId and companyToken are required",
    "type": "validation_error"
  }
}
```

| Code                   | Description                                          |
| ---------------------- | ---------------------------------------------------- |
| `invalid_format`       | Request body is malformed or missing required fields |
| `invalid_audio_format` | Unsupported audio format (use MP3, WAV, OGG, AAC)    |
| `invalid_external_id`  | External ID not found or not accessible              |
| `invalid_parameter`    | A query or path parameter has an invalid value       |

### Authentication Errors (401)

Returned when authentication fails.

```json theme={null}
{
  "error": {
    "code": "invalid_token",
    "message": "Invalid or expired API token",
    "type": "authentication_error"
  }
}
```

| Code                       | Description                           |
| -------------------------- | ------------------------------------- |
| `invalid_token`            | Token is invalid, expired, or revoked |
| `missing_token`            | No X-API-Key header provided          |
| `insufficient_permissions` | Token lacks required permissions      |

### Resource Errors (404)

Returned when a requested resource doesn't exist.

```json theme={null}
{
  "error": {
    "code": "audio_not_found",
    "message": "Audio file not found",
    "type": "resource_error"
  }
}
```

| Code                 | Description                              |
| -------------------- | ---------------------------------------- |
| `audio_not_found`    | Audio file doesn't exist or was deleted  |
| `endpoint_not_found` | The requested API endpoint doesn't exist |

### Conflict Errors (409)

Returned when the request conflicts with current state.

```json theme={null}
{
  "error": {
    "code": "audio_in_use",
    "message": "Cannot delete audio with pending scheduled sends",
    "type": "conflict_error"
  }
}
```

| Code                    | Description                            |
| ----------------------- | -------------------------------------- |
| `audio_in_use`          | Cannot delete audio with pending sends |
| `duplicate_external_id` | External ID is already in use          |

### Payload Errors (413)

Returned when the request body is too large.

```json theme={null}
{
  "error": {
    "code": "file_too_large",
    "message": "File size exceeds 10MB limit",
    "type": "validation_error"
  }
}
```

### Rate Limit Errors (429)

Returned when you've exceeded your rate limit.

```json theme={null}
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded. Please retry after 42 seconds.",
    "type": "rate_limit_error",
    "retryAfter": 42
  }
}
```

### Server Errors (5xx)

Returned when something goes wrong on our end.

```json theme={null}
{
  "error": {
    "code": "internal_error",
    "message": "An unexpected error occurred",
    "type": "server_error"
  }
}
```

***

## Handling Errors

<CodeGroup>
  ```javascript Node.js theme={null}
  async function makeRequest(url, options) {
    const response = await fetch(url, options);
    
    if (!response.ok) {
      const data = await response.json();
      const error = data.error;
      
      switch (response.status) {
        case 400:
          console.error('Validation error:', error.message);
          break;
        case 401:
          console.error('Authentication failed:', error.message);
          // Refresh token or prompt for re-auth
          break;
        case 404:
          console.error('Resource not found:', error.message);
          break;
        case 429:
          const retryAfter = error.retryAfter || 60;
          console.warn(`Rate limited. Retrying in ${retryAfter}s`);
          await sleep(retryAfter * 1000);
          return makeRequest(url, options); // Retry
        case 500:
        case 503:
          console.error('Server error:', error.message);
          // Implement exponential backoff
          break;
      }
      
      throw new Error(error.message);
    }
    
    return response.json();
  }
  ```

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

  def make_request(url, **kwargs):
      response = requests.request(**kwargs, url=url)
      
      if not response.ok:
          error = response.json().get('error', {})
          
          if response.status_code == 400:
              print(f"Validation error: {error.get('message')}")
          elif response.status_code == 401:
              print(f"Authentication failed: {error.get('message')}")
              # Refresh token or prompt for re-auth
          elif response.status_code == 404:
              print(f"Resource not found: {error.get('message')}")
          elif response.status_code == 429:
              retry_after = error.get('retryAfter', 60)
              print(f"Rate limited. Retrying in {retry_after}s")
              time.sleep(retry_after)
              return make_request(url, **kwargs)  # Retry
          elif response.status_code >= 500:
              print(f"Server error: {error.get('message')}")
              # Implement exponential backoff
          
          response.raise_for_status()
      
      return response.json()
  ```
</CodeGroup>

***

## Best Practices

<AccordionGroup>
  <Accordion title="Implement Retry Logic">
    For 429 and 5xx errors, implement retry with exponential backoff:

    ```javascript theme={null}
    async function retryWithBackoff(fn, maxRetries = 3) {
      for (let i = 0; i < maxRetries; i++) {
        try {
          return await fn();
        } catch (error) {
          if (i === maxRetries - 1) throw error;
          const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
          await new Promise(r => setTimeout(r, delay));
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="Log Error Details">
    Always log the full error response for debugging:

    * Error code and message
    * Request ID (if provided)
    * Timestamp
    * Request details (endpoint, method, params)
  </Accordion>

  <Accordion title="Handle Gracefully">
    * Show user-friendly messages (not raw error codes)
    * Provide actionable next steps when possible
    * Don't expose internal error details to end users
  </Accordion>
</AccordionGroup>
