Errors you can act on

· Hyperouter · 5 min read

Every error from Hyperouter uses OpenAI's error object and a matching HTTP status. Code that handles OpenAI's errors already handles most of these. This guide covers what is worth acting on and what is not, and ends with a retry loop.

The error object

{
  "error": {
    "message": "n must be an integer from 1 to 128",
    "type": "invalid_request_error",
    "code": "invalid_body",
    "param": "n"
  }
}
FieldMeaning
messageA sentence that says what is wrong. Do not parse it.
typeThe class of error.
codeA stable identifier for the cause. Match on this field.
paramThe request field at fault, or null.

Some errors add generation_id: the id of a job that is still running or already ran. Poll it with GET /v1/generations/{id}.

Statuses worth handling

StatusWhat it meansWhat to do
400The request is invalid. code names the cause and param the field.Fix the request. Do not retry it unchanged.
401The API key is missing or not valid.Check the key.
402insufficient_credits: your balance, or the key's limit, cannot cover the most the request could cost.Top up, or raise the key's limit.
403The account is suspended, or a feature or model is not enabled for it.Open a support ticket.
404No such id, another account's id, or a video past its expires_at.Do not retry.
409Not in a state that allows the call, or an Idempotency-Key conflict.See retry safely.
429Every provider is at capacity, or your account is over its rate limits.Wait for Retry-After, then retry.
500, 501Hyperouter failed, or the endpoint is not implemented. Not billed.Retry a 500 with care, as below.
503A provider failed or did not answer in time.Retry, as below.

Three codes you will meet

The same family includes venue_busy (429): every provider for a chat model is over its rate budget.

Why 503, not 502 or 504

Hyperouter never answers 502 or 504. Those would reach you as a bare page from our edge network, without the error body, so you could not read code.

Where a gateway would answer them, you get 503 with the usual error object, and the gateway status in the response header X-Upstream-Status: 502 when a provider failed, 504 when it timed out. The code says which kind of failure it was: all_venues_failed, upstream_error or timeout.

HTTP/2 503
x-upstream-status: 504

Read X-Upstream-Status if you want to tell a failure from a timeout. For most code, 503 and code are enough.

When not to retry

Retrying is not always safe. A retry can start a second job or a second charge. Hyperouter marks those answers with x-should-retry: false:

On chat, a provider that has accepted a request may be running it. If it then stops answering, the request is not sent to another provider: the answer is 503 with code timeout or upstream_error, and it is billed on its hold. Look it up with GET /v1/generations/{id} before you decide to send it again.

A retry loop with backoff

Retry 429 and 503, honour Retry-After, give up on x-should-retry: false, and send an Idempotency-Key on anything that costs money.

import random
import time
import uuid

import requests

def post_with_retry(url, headers, body, attempts=5):
    headers = {**headers, "Idempotency-Key": str(uuid.uuid4())}  # same key on every attempt
    for attempt in range(attempts):
        r = requests.post(url, headers=headers, json=body, timeout=120)
        if r.status_code < 400:
            return r.json()
        if r.headers.get("x-should-retry") == "false":
            r.raise_for_status()
        if r.status_code not in (429, 503) or attempt == attempts - 1:
            r.raise_for_status()
        wait = float(r.headers.get("Retry-After", 2 ** attempt))
        time.sleep(wait + random.random())  # jitter

On a chat request, the retry gets 409 idempotency_key_replayed if the first attempt did run. That is the cue to read the record with GET /v1/generations/{id} and stop.

The OpenAI SDKs already retry a 408, 409, 429 or 5xx twice and respect x-should-retry: false, so you may need no loop at all.

What next