Cette page n'est pas encore traduite en Français. La version anglaise d'origine est affichée.

Errors and debugging

The status code / `error_type` pairing

Every /api/v1 error body has the same shape, on every path.

json
{
  "error": {
    "code": 404,
    "message": "요청을 처리할 수 있는 엔드포인트가 없습니다.",
    "metadata": { "error_type": "no_endpoints_found", "models": ["google/gemma-4-26b-a4b"] }
  }
}
  • error.code is always equal to the HTTP status code. They never disagree.
  • error.message is a human sentence. The wording can change without notice, so do not branch on it.
  • error.metadata.error_type is the machine-readable value. Branch on that.

Status codes and error_type

Statuserror_typeRaised when
400invalid_requestSchema violation: body is not a JSON object, messages missing, a message without role, /generation called without id
400model_not_foundUnknown model id
401invalid_api_keyKey not found, revoked, Authorization header missing, or wrong key kind
401expired_api_keyThe key is past its expires_at
402insufficient_creditsBalance below the debt floor, or the key is suspended for lack of credit
402key_limit_exceededThe spend limit set on the key was exceeded
403key_suspendedAn administrator suspended the key. Topping up will not clear it
404no_endpoints_foundThe candidate chain came out empty
404not_supportedUnimplemented path, endpoints lookup for an unknown model, or a generation that does not exist or is not yours
408timeoutThe upstream did not answer within the time limit
429rate_limit_exceededRequest rate limit exceeded
502provider_errorEvery candidate in the chain failed upstream
503model_loadingA local GPU slot is starting, or could not start within its budget
500serverAnything else. Details stay in the server log only

Extra fields in metadata

error_typeAdditional fields
model_not_foundmodel — the id we could not find
insufficient_creditsbalance_krw — current balance in won
key_limit_exceededlimit_krw, usage_krw
no_endpoints_foundmodels — the models that were requested
not_supportedpath — the missing path (on the unimplemented-path catch-all)
timeouttimeout_sec
provider_errorprovider_code — the status the upstream returned
model_loadingretry_after_sec

Any response carrying retry_after_sec also gets the standard Retry-After header, so you can back off correctly without parsing metadata.

Error handling in practice

python
import os, time, requests

def call(payload, attempts=3):
    for attempt in range(attempts):
        response = requests.post(
            "https://openrouter.myip.co.kr/api/v1/chat/completions",
            headers={"Authorization": f"Bearer {os.environ['MYIP_API_KEY']}"},
            json=payload,
            timeout=650,
        )
        if response.ok:
            return response.json()

        body = response.json()
        error_type = body["error"]["metadata"]["error_type"]

        # Needs a top-up, a fix, or an administrator. Retrying will not help.
        if error_type in {"invalid_api_key", "expired_api_key", "key_suspended",
                          "insufficient_credits", "key_limit_exceeded",
                          "invalid_request", "model_not_found", "not_supported"}:
            raise RuntimeError(f"{response.status_code} {error_type}: {body['error']['message']}")

        # Worth waiting and trying again.
        if error_type in {"model_loading", "rate_limit_exceeded", "timeout", "provider_error"}:
            wait = int(response.headers.get("Retry-After", 2 ** attempt))
            time.sleep(wait)
            continue

        raise RuntimeError(f"{response.status_code} {error_type}")

    raise RuntimeError("out of retries")

Errors during a stream

Once a stream has started (the first byte is out), the status code can no longer change. The error therefore arrives inside an SSE chunk.

data: {"id":"gen-…","object":"chat.completion.chunk","model":"google/gemma-4-26b-a4b","provider":"MyIP Local GPU","error":{"code":502,"message":"upstream disconnected","metadata":{"error_type":"provider_error"}},"choices":[{"index":0,"delta":{"content":""},"finish_reason":"error"}]}

data: [DONE]

Even on HTTP 200, check chunks for an error field. Details are in Streaming.

Why 503 model_loading happens

We serve models on our own GPU farm. If the GPU slot for a model is down when your request arrives, the gateway wakes it and waits. When it cannot come up within its start-up budget (which varies per model and comes from that slot's configuration) and no other candidate can take the request, you get 503 model_loading.

HTTP/1.1 503 Service Unavailable
Retry-After: 300
X-MyIP-Currency: KRW
json
{
  "error": {
    "code": 503,
    "message": "로컬 모델을 기동하는 중입니다. 잠시 후 다시 시도하세요.",
    "metadata": { "error_type": "model_loading", "retry_after_sec": 300 }
  }
}

Wait for Retry-After and call again — start-up is already in progress, so the retry usually succeeds. The value differs per model, so read the header rather than hard-coding it. Background is in Local GPU models.

502 provider_error and the candidate chain

One request may try several candidates in order. Before the first byte is sent, a failed candidate is dropped and the next is tried. 502 is returned only when every candidate in the chain has failed; metadata.provider_code holds the status the last candidate returned.

What was tried, and how many times, is recorded in provider_responses on GET /generation. Failed requests are recorded too — at zero cost, with no ledger entry.

Identifiers to debug with

Everything you need to narrow a problem down is in the response headers.

HeaderUse
X-MyIP-Request-IdTies together every candidate attempt of one request. Present on error responses too
X-MyIP-Generation-IdThe /generation lookup key: tokens, cost and attempt history
X-MyIP-Model / X-MyIP-ProviderThe candidate that actually answered
bash
curl -i https://openrouter.myip.co.kr/api/v1/chat/completions \
  -H "Authorization: Bearer $MYIP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"google/gemma-4-26b-a4b","messages":[{"role":"user","content":"hi"}]}' \
  | grep -i '^x-myip'

If you get 404 not_supported

You called a path we do not implement. The body names it.

json
{
  "error": {
    "code": 404,
    "message": "Endpoint not supported on MyIP OpenRouter",
    "metadata": { "error_type": "not_supported", "path": "/api/v1/embeddings" }
  }
}

The full list is in Unsupported endpoints.

Dernière mise à jour : 5 sept. 2026