# Keep the HTTP status and JSON error code separate in your AI API adapter

> Source: <https://dev.to/routerbase_com/keep-the-http-status-and-json-error-code-separate-in-your-ai-api-adapter-j7p>
> Published: 2026-11-02 03:45:58+00:00

An adapter label such as `provider_rejected_402` leaves an important question unanswered: did the server send HTTP 402, or did the adapter find 402 inside a JSON body?

Those are different observations. If the adapter merges them, the next person debugging the request has to guess what actually happened.

For an error response, start with four fields:

RouterBase's [error reference](https://docs.routerbase.com/essentials/errors) documents both nested and top-level JSON error shapes, along with `x-request-id` for correlating a request. Keep the transport status in its own field rather than replacing it with a value from the body.

This function summarizes response data you supply. It makes no network requests and needs no API key. The example response is synthetic.

```
"""Offline example: summarize a supplied error response; never call an API."""

import json
from uuid import UUID

def summarize_error(http_status, headers, body):
    payload = {}
    body_kind = "too_large"
    if len(body) <= 65536:
        try:
            value = json.loads(body)
            body_kind = "object" if isinstance(value, dict) else "non_object"
            if isinstance(value, dict):
                payload = value
        except (ValueError, RecursionError):
            body_kind = "invalid_json"

    error = payload.get("error")
    fields = error if isinstance(error, dict) else payload
    code = fields.get("code")
    if isinstance(code, str) and code.isascii() and code.isdigit() and len(code) == 3:
        code = int(code)
    if type(code) is not int or not 100 <= code <= 599:
        code = None

    normalized = {name.lower(): value for name, value in headers.items()}
    request_id = normalized.get("x-request-id") or payload.get("request_id")
    try:
        request_id = str(UUID(request_id)) if isinstance(request_id, str) else None
    except ValueError:
        request_id = None

    return {
        "http_status": http_status,
        "json_numeric_code": code,
        "request_id": request_id,
        "body_kind": body_kind,
    }

if __name__ == "__main__":
    print(summarize_error(
        402,
        {"X-Request-ID": "00000000-0000-4000-8000-000000000001"},
        '{"error":{"code":402,"message":"Synthetic example"}}',
    ))
```

The output keeps `http_status` and `json_numeric_code` as separate values. In the synthetic example both are 402. If you supply HTTP 200 and a body containing `{"code":402}`, it preserves both values; that is an adapter test case, not a claim that RouterBase returns that combination.

The function deliberately omits error messages, prompts, request bodies, and authorization headers. It accepts only numeric codes in the 100–599 range and UUID-shaped request IDs. String error categories are left out of this small example. A missing value remains `None`; it is not replaced with a guessed cause.

This is a limited diagnostic summary, not a general redaction system or a complete response validator. Pass the actual integer status from your HTTP client, string header names and a text body. Bound the response read in your HTTP client too: the length check here happens after the body has reached the function.

For RouterBase organization requests, the [organization guide](https://docs.routerbase.com/essentials/organizations) describes 402 responses for an insufficient organization wallet and for member spending limits. The HTTP status alone does not distinguish those cases. Read the specific error in a protected debugging context, then check the relevant account scope and limits.

Avoid making automatic changes to spending caps just because an adapter emitted a generic billing label. Likewise, a successful catalog lookup does not validate a later generation request.

When escalating an issue, include the timestamp with its time zone, endpoint path, HTTP status, and request ID. Review any error text separately before sharing it. Keep credentials and private inputs out of a public support message.

Use a nested error object, a top-level numeric code, an HTML error page, a JSON array, and an oversized body. Verify that a mismatch between transport status and body code stays visible.

We checked this example locally with synthetic inputs, including malformed JSON and an invalid request ID. No inference request was sent, and no model performance or billing behavior was tested.

We are the RouterBase team. Explore [RouterBase](https://routerbase.com/?utm_source=dev&utm_medium=article&utm_campaign=2026_11_error_evidence&utm_content=rb_20261102_error_evidence_dev_v01) for an OpenAI-compatible API for AI models.
