cd /news/ai-tools/keep-the-http-status-and-json-error-… · home › topics › ai-tools › article
[ARTICLE · art-162255] src=dev.to ↗ pub= topic=ai-tools verified=true sentiment=· neutral

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

A developer published an offline Python helper that keeps an API response's HTTP status separate from any numeric error code found in the JSON body, so adapters can distinguish a transport-level 402 from a code embedded in the payload. The function, which makes no network calls, returns only the HTTP status, a validated 100–599 JSON code, a UUID-shaped request ID, and a body-kind label, deliberately omitting error messages, prompts, and authorization headers. The writeup also advises against automatically changing spending caps on a generic billing label and recommends including timestamp, endpoint, status, and request ID when escalating issues.

by read3 min views1 publishedNov 2, 2026

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 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 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 for an OpenAI-compatible API for AI models.

── more in #ai-tools 4 stories · sorted by recency
── more on @routerbase 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/keep-the-http-status…] indexed:0 read:3min 2026-11-02 · —