Error Reference
Complete, verified catalog of every error the Voidnet can return.
Every gateway error uses one JSON shape. The error field is an object with code, message, and status — not a string.
{
"error": {
"code": "api_key_invalid",
"message": "Invalid token format. Expected: vnb-sk-xxx, vai-sk-xxx, or JWT",
"status": 401
}
}OAuth endpoints (POST /oauth/token, /.well-known/*) use a different shape: {"error": "<code>", "error_description": "<message>"}. These are noted below.
Authentication — 401
Returned when the Authorization header is missing, malformed, or the credential is unknown/expired.
| Code | Condition | Fix |
|---|---|---|
api_key_missing | No Authorization header | Add Authorization: Bearer <credential> |
api_key_invalid | Key format is wrong, JWT header/signature invalid, or key not found | Key prefixes: vnb-sk-, vai-sk-, or a 3-segment JWT |
api_key_expired | JWT access token expired | Request a new token from POST /oauth/token |
api_key_revoked | Key was revoked in the Console | Generate a new key |
session_invalid | MCP session ID not found, expired, or buyer mismatch | Start a new session with initialize |
The gateway auto-detects the credential type: a 3-segment (dot-separated) value is treated as a JWT; a vnb-sk-/vai-sk- prefix is treated as an API key.
WWW-Authenticate header is set on 401 (missing/invalid/expired key) and 403 insufficient_scope responses so OAuth-aware clients can bootstrap discovery.
OAuth — 400 / 401 / 405 / 500
Returned by POST /oauth/token and the /.well-known/* metadata endpoints. These use the OAuth error shape, not the gateway envelope.
| Status | Code | Condition |
|---|---|---|
| 400 | invalid_request | Body unreadable or Content-Type is not application/x-www-form-urlencoded / application/json |
| 400 | unsupported_grant_type | grant_type is not client_credentials |
| 400 | invalid_client | client_id empty, bad prefix, or client_secret mismatch |
| 401 | invalid_client | Credential lookup failed (key not found) |
| 405 | unsupported_method | Wrong HTTP method on a /.well-known/* or /oauth/token endpoint |
| 500 | server_error | Token signing not configured or signing failed |
| 404 | not_found | JWKS requested but signing not configured (on /.well-known/jwks.json) |
Billing — 403 / 429
Returned after authentication, when the purchase/subscription check fails.
| Status | Code | Condition | Fix |
|---|---|---|---|
| 403 | app_not_purchased | No purchase or subscription record | Purchase (or "Get" a free tier) in the Marketplace |
| 403 | subscription_inactive | Subscription exists but isn't active | Check billing status in the Console |
| 403 | subscription_expired | Subscription period ended | Renew in the Console |
| 403 | subscription_cancelled | Subscription was cancelled | Contact support or repurchase |
| 429 | meter_expired | Billing period expired, meter needs reset | Wait for meter reset |
Rate limiting — 429
Returned when a usage limit is exceeded. All limits use sliding-window counters.
| Status | Code | Condition | Fix |
|---|---|---|---|
| 429 | rate_limit_exceeded | Request rate exceeded (per-minute or per-day) | Wait for the window to reset, then retry with backoff |
| 429 | meter_limit_exceeded | Monthly request quota exhausted | Wait for the rolling 30-day reset, or upgrade tier |
429 responses do not include a details payload. There are no X-RateLimit-* response headers. Track your usage in the Console.
Suspension — 403
| Status | Code | Condition |
|---|---|---|
| 403 | buyer_suspended | Buyer account suspended |
| 403 | publisher_suspended | Publisher account suspended |
| 403 | insufficient_scope | JWT scope doesn't permit the operation |
Routing — 400 / 404
| Status | Code | Condition | Fix |
|---|---|---|---|
| 400 | invalid_path | URL doesn't match /v1-beta/{adapter}/{username}/{appname} | Check the path |
| 400 | invalid_adapter_type | Adapter isn't mcp | Only mcp is available |
| 400 | invalid_adapter | Adapter in URL doesn't match request type | Use the correct adapter |
| 400 | invalid_request | Missing fields or invalid JSON-RPC structure | Check the JSON-RPC body |
| 404 | app_not_found | No app registered for that username/appname | Verify both names |
| 404 | publisher_not_found | Publisher username doesn't exist | Check the username |
| 404 | publisher_api_key_not_found | Publisher has no active API key | Publisher must regenerate their key |
| 404 | purchase_not_found | Purchase record missing during usage query | Confirm the purchase |
Session — 400 / 401 / 500
| Status | Code | Condition | Fix |
|---|---|---|---|
| 400 | session_not_initialized | Session exists but initialize was never completed | Send initialize first |
| 401 | session_invalid | Session ID unknown/expired or buyer mismatch | Start a new session |
| 500 | session_error | Failed to create or establish an MCP session | Retry; contact support if persistent |
Publisher — 4xx / 5xx
Returned when the gateway proxies to the publisher and the publisher's server responds with an error.
| Status | Code | Condition |
|---|---|---|
| 4xx | publisher_client_error | Publisher returned a 400–499 (their error — check their docs) |
| 5xx | publisher_server_error | Publisher returned a 500+ (their server error) |
| 502 | publisher_error | Publisher transport/initialization failure |
| 503 | circuit_open | Publisher temporarily blocked after repeated failures — retry later |
| 504 | gateway_timeout | Publisher didn't respond in time |
| 501 | adapter_not_supported | Requested adapter isn't available (only mcp is) |
Request — 400 / 404 / 405
| Status | Code | Condition |
|---|---|---|
| 400 | bad_request | Unparseable request |
| 413 | body_too_large | Request body exceeds the size limit |
| 404 | not_found | No route matches the URL |
| 405 | method_not_allowed | Wrong HTTP method |
| 406 | not_acceptable | SSE client didn't accept text/event-stream |
Internal — 500
| Status | Code | Condition |
|---|---|---|
| 500 | internal_error | Server-side error — retry, then contact support |
| 500 | database_error | Database query/write failed |
| 500 | redis_error | Rate-limit cache operation failed |
| 500 | trace_error | Failed to start request tracing (non-critical) |
| 500 | invalid_app_config | App configuration is invalid |