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

# Error messages

> Diagnose API failures, retry transient requests, and handle incomplete results.

Start with the HTTP status and error details. Invalid inputs, credentials, and exhausted credits need a change before you retry. Rate limits, temporary service failures, and connection problems may recover after a wait.

## Read an API error

Direct HTTP chat-completion requests return structured errors in this form. For example, an unsupported `tool_choice` produces HTTP 400:

```json theme={null}
{
  "error": {
    "message": "Only `tool_choice` 'auto' and 'none' are supported; forcing a tool call is not supported yet.",
    "type": "invalid_request_error",
    "param": "tool_choice",
    "code": "unsupported_tool_choice"
  }
}
```

Use `message` to diagnose the problem, `param` to locate an offending field, and `type` or `code` to classify it when available. Those last three fields can be `null`; not every validation failure has a dedicated code. Branch on a documented code rather than matching the wording of a message. A connection failure has no HTTP response, and malformed HTTP requests or intermediary failures may not have this JSON body.

Record the response's `x-trace-id` header when present. It helps support locate the request, but may be absent when a request fails before reaching the API. Also retain the UTC time, endpoint, model, HTTP status, and error details. Keep API keys out of logs and support messages.

The SDK exposes errors as `SDKError` subclasses with `status_code`, `error_type`, `code`, `param`, `request_id`, and `retry_after`. Some typed-argument checks, such as unsupported `tool_choice`, raise `BadRequestError` before sending HTTP; those errors have no HTTP status or request ID. For a `RateLimitError`, `code` is `"rate_limit"` and the server’s original code is in `details["code"]` when present. Exhausted credits raise `QuotaExceededError`.

## Decide whether to retry

| Status or condition                     | What to check                                                                    | Next step                                                                                                                            |
| --------------------------------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `400` or `422`                          | Invalid JSON, parameter values, message structure, media, or unsupported options | Correct the request before resending it. Inspect `param` and the response body.                                                      |
| `401`                                   | Missing, invalid, or revoked API key                                             | Check `Authorization: Bearer ...` and the key supplied through `PERCEPTRON_API_KEY`.                                                 |
| `403`                                   | Access denied                                                                    | Check the key's organization and access to the requested resource.                                                                   |
| `404`                                   | Unknown endpoint, model, or file                                                 | Check the URL and identifier. Use the current [model ID](/perceptron-mk1.5/models/perceptron-mk1.5) or verify the file still exists. |
| `411` on a file upload                  | Missing `Content-Length`                                                         | Use the multipart upload pattern in the [Files guide](/perceptron-mk1.5/guides/files).                                               |
| `413`                                   | Upload exceeds a file-size or storage limit                                      | Reduce the upload, or free storage if the error reports exhausted storage quota.                                                     |
| `429` with `type: "insufficient_quota"` | Credits are exhausted                                                            | Check your plan and billing. Repeated requests will not restore credits.                                                             |
| `429` with `type: "rate_limit_error"`   | Too many requests                                                                | Reduce concurrency and wait before retrying.                                                                                         |
| `500`, `502`, `503`, or `504`           | Temporary service or upstream failure                                            | Retry a limited number of times with backoff; investigate persistent failures.                                                       |
| Connection error or timeout             | No complete response was received                                                | Check connectivity and timeouts, then retry only within your application's policy. The original request may already have run.        |

Organization rate-limit responses include `Retry-After` in seconds. An HTTP 503 with `code: "model_overloaded"` includes `Retry-After: 30`. Other failures may omit that header; use backoff when it is absent. A retry is a new model request and may produce a different answer or additional usage.

## Correct common request problems

### Context and output limits

`context_length_exceeded` means the input or requested completion does not fit the model's context. Shorten conversation history, reduce the amount of media, or lower `max_completion_tokens` when the requested output is the problem. Keep enough room for reasoning and the final answer. See the [model card](/perceptron-mk1.5/models/perceptron-mk1.5) for current context and output limits.

Exceeding a configured output limit can also produce a validation error without this code. Increasing a client timeout will not fix either request-size problem. A response that starts successfully but ends with `finish_reason: "length"` is a separate case: handle it as [incomplete output](#handle-incomplete-responses-and-streams).

### Images, videos, audio, and files

Check that a media URL is reachable and has not expired, that the bytes decode as the declared format, and that a file reference exists in the organization making the request. An image URL that serves an HTML login page is not an image. For uploads, send the actual media bytes with a matching content type; renaming the extension does not convert a file.

Use the [Files guide](/perceptron-mk1.5/guides/files) for upload formats and limits, and [video understanding](/perceptron-mk1.5/capabilities/video-understanding) for video URLs and frame inputs. When a request contains many media parts, reduce their number; uploading them first does not remove request-level media limits.

### Audio input and video soundtracks

| Error code or message                                                | Correction                                                                                                                                                                                                              |
| -------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `audio_token_limit_exceeded`                                         | Shorten or split the audio. The current Mk1.5 limit is 16,384 audio tokens per item, approximately 21.8 minutes. The response message describes the active model's limit; increasing the output budget will not fix it. |
| `Invalid input_audio.format: 'ogg'. Expected one of wav, mp3, flac.` | Convert to WAV, MP3, or FLAC and set `input_audio.format` accordingly. Supply bare base64 in `data`, without a data-URL prefix.                                                                                         |
| `invalid_audio_format`                                               | Check the bytes served by an audio URL. Renaming an unsupported file or sending an MP4 video as `audio_url` does not convert it to a supported audio container.                                                         |
| Message contains `does not support audio input`                      | Select an audio-capable model such as `perceptron-mk1.5` for standalone audio.                                                                                                                                          |
| Message contains `does not support audio in video`                   | Select a model that supports video soundtracks, or disable `vision_config.enable_audio_in_video` for a visual-only request.                                                                                             |
| `media_too_large`                                                    | Reduce the downloaded media size and check [upload limits](/perceptron-mk1.5/guides/files). Byte limits and audio-token limits are separate.                                                                            |

An over-limit chat request returns HTTP 400 with `type: "invalid_request_error"` and `code: "audio_token_limit_exceeded"`; `param` can be `null`. Audio is rejected rather than silently truncated. This validation happens before generation, so a streaming request can also fail with an ordinary JSON error before any SSE events arrive.

No soundtrack, or no soundtrack overlap with the processed video window, falls back to frames only. Decode failures and over-limit soundtracks still return errors. See [Audio](/perceptron-mk1.5/capabilities/audio) for valid content parts and [video soundtracks](/perceptron-mk1.5/capabilities/video-understanding#analyze-video-soundtracks) for the opt-in flag.

### Reasoning effort

`reasoning_effort` accepts exactly `none`, `minimal`, `low`, `medium`, and `high`. In a direct HTTP request, an unknown value returns HTTP 400 with `invalid_request_error` and a parse-error message naming the field. The SDK rejects an invalid typed argument before HTTP with `BadRequestError(code="invalid_reasoning_effort")`. Correct the value before retrying. Chat completions and Multilook take this field at the top level; Detect uses `config.reasoning_effort`.

A completed HTTP request with reasoning but an empty answer may have exhausted its output budget. Check `finish_reason`; if it is `length`, allow more tokens for reasoning and the answer, or use `none` if reasoning is unnecessary. See [Reasoning](/perceptron-mk1.5/capabilities/thinking).

### Function-calling requests

| Code                            | Correction                                                                                                                                                                                       |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `unsupported_tool_choice`       | Use `auto` or `none`; forcing a named function or `required` is unsupported.                                                                                                                     |
| `unsupported_tools_combination` | Complete the tool workflow, then request a [structured final answer](/perceptron-mk1.5/capabilities/structured-outputs#combine-tools-with-a-constrained-final-answer) without tool declarations. |
| `unsupported_parameter`         | Check whether the selected model and endpoint support the parameter. Use chat completions for function calling.                                                                                  |
| `missing_tool_result`           | Return one result for every outstanding call before continuing the conversation.                                                                                                                 |
| `invalid_tools`                 | Inspect the indicated declaration or history entry, including its call ID and arguments.                                                                                                         |

The [function-calling guide](/perceptron-mk1.5/guides/tool-calling#preserve-the-conversation) shows how to preserve assistant calls, reasoning, and matching tool results.

## Retry a transient inference failure

This example uses the Perceptron SDK and adds an application retry policy. It makes up to three attempts and waits no more than 60 seconds between attempts. If the server asks for a longer wait, it returns the error to the caller instead of retrying early. The request timeout applies separately to each attempt.

Install `perceptron>=0.4.0`, set `PERCEPTRON_API_KEY`, and save this as `retry_example.py`:

```python theme={null}
import math
import os
import random
import time

from perceptron import Client, QuotaExceededError, SDKError, TimeoutError, TransportError


client = Client(
    api_key=os.environ["PERCEPTRON_API_KEY"],
    provider="perceptron",
    timeout=30.0,
)
RETRYABLE_STATUS = {408, 429, 500, 502, 503, 504}


def retry_delay(retry_after, attempt):
    try:
        delay = float(retry_after)
    except (TypeError, ValueError):
        delay = -1
    if math.isfinite(delay) and delay >= 0:
        return delay
    return 2 ** attempt + random.random()


def complete_with_retries(messages):
    for attempt in range(3):
        try:
            return client.chat.completions.create(
                model="perceptron-mk1.5",
                messages=messages,
                max_completion_tokens=256,
            )
        except QuotaExceededError:
            raise
        except (TimeoutError, TransportError):
            if attempt == 2:
                raise
            delay = retry_delay(None, attempt)
        except SDKError as error:
            if error.status_code not in RETRYABLE_STATUS or attempt == 2:
                raise
            delay = retry_delay(error.retry_after, attempt)
            if delay > 60:
                raise
        time.sleep(delay)


if __name__ == "__main__":
    response = complete_with_retries([
        {"role": "user", "content": "Give one tip for reviewing a video."},
    ])
    choice = response.choices[0]
    if choice.finish_reason != "stop" or choice.message.tool_calls:
        raise RuntimeError("The request did not return a complete text answer")
    print(choice.message.content or "")
```

Run `python retry_example.py`. The helper retries only the inference request. It does not automatically retry an incomplete answer, rerun application functions, or restart a whole agent workflow. Use the [workflow budget guidance](/perceptron-mk1.5/guides/tool-agents#bound-the-whole-workflow) when several requests and tool executions share a deadline.

## Handle incomplete responses and streams

HTTP 200 means the request was accepted; check the completion as well:

* `finish_reason: "stop"` marks a completed answer.
* `finish_reason: "tool_calls"` marks a completed model turn asking your application to handle calls.
* `finish_reason: "length"` means the output budget was exhausted. Keep any partial text clearly marked as incomplete; do not execute partial arguments or treat truncated JSON or annotations as validated output.

A stream can fail after its HTTP headers have been sent. In that case the API sends an SSE data event containing `{"error": {...}}` and closes without `[DONE]`. The Perceptron SDK raises an `SDKError` subclass for that error event, and `IncompleteStreamError` when the stream ends without `[DONE]`. A raw SSE consumer must recognize the error event itself. A dropped connection or stream ending without a finish reason is also incomplete.

Keep consuming after the finish chunk so you receive any trailing usage or error event. Do not splice a fresh retry onto an old partial stream. See the [tool-call streaming example](/perceptron-mk1.5/guides/tool-calling#stream-a-tool-call), [tracking stream example](/perceptron-mk1.5/capabilities/video-tracking#stream-a-tracking-answer), and [structured-output validation](/perceptron-mk1.5/capabilities/structured-outputs#validate-completed-streams).

## Handle Multilook partial failures

A successful [Multilook request](/perceptron-mk1.5/guides/multilook) can return HTTP 200 while individual entries in `results` contain an `error`. Inspect each result before accessing its `completions`. The prompt error has a `message` and may include `type` and `code`.

Keep successful results, and decide whether each failed prompt needs correction or a retry. Resubmit only the prompts you choose to retry, preserving their shared context and any prompt-specific media. Track their original indices in your application: `prompt_index` in the new response refers to the new request. Also check each returned completion's `finish_reason`; truncated output is not necessarily a prompt-level `error`.

For example, a prompt-specific audio input can produce `results[].error.code: "audio_token_limit_exceeded"` while another prompt succeeds. Request-level validation, such as an invalid `reasoning_effort`, rejects the whole request before prompt processing.

If every prompt fails, the API returns a whole-request error instead of a successful results body. Its status is classified from the first prompt error; unclassified failures return HTTP 502.

## Separate tool failures from API failures

A database timeout or failed lookup inside your application is a tool execution failure. Return an explicit error result with the original `tool_call_id` if you want the model to continue; the [tool-agent guide](/perceptron-mk1.5/guides/tool-agents#return-failures-as-results) shows the message shape.

Keep the assistant calls and completed tool results when retrying the next model request. Do not restart the whole tool loop blindly: a function that sends a message, creates a record, or changes an external system may already have succeeded. Decide how to reconcile or deduplicate that action in your application before repeating it.
