KubeadaptDocsBack to site
Sign inStart free
DocsAPI ReferenceCLI
    • Pagination & Filtering
    • Error Handling
    • Cost Modes
Docs homev1Api ReferenceErrors

API Reference

Error Handling

The full Kubeadapt /v1 error code catalog, error families, retry rules, the details payload shape, and the universal error envelope.


Every /v1 error uses the same {data: null, meta, error} envelope. The error block carries a stable code from a closed catalog of 23 values plus a human message and optional details. Codes are grouped into families (auth, request shape, validation, not-found, pagination, rate, server, upstream) with consistent retry semantics.

The error envelope

Failed requests use the same envelope as successful ones, with data: null and a populated error block. The HTTP status code is in the response status line, the URL is the one you sent, and the request_id lives at meta.request_id — none of these are duplicated inside the body.

json
1{
2  "data": null,
3  "meta": {
4    "request_id": "req_01J5K3V0Q7Y4XR8A2B3C5D7E9F",
5    "applied_at": "2026-05-21T10:30:00Z"
6  },
7  "error": {
8    "code": "INVALID_COST_MODE",
9    "message": "cost_mode is not supported on this endpoint (one physical bill)",
10    "details": [
11      { "field": "cost_mode", "reason": "not_supported_on_this_endpoint" }
12    ]
13  }
14}
  • error.code is the stable identifier. Branch on the code, never on the message string or the HTTP status alone.
  • error.details is an optional array. Some errors carry per-field reasons; many do not.

For the envelope contract (success and error shape, omitted vs null error), see API Overview. When reporting an issue to support@kubeadapt.io, include meta.request_id.

Error code catalog

This is the closed set. The API never returns a code outside this list; clients can safely raise an exception on any unknown code.

HTTPerror.codeFamilyRetryable?
400BAD_REQUESTRequest shapeNo
400INVALID_CURSORPaginationNo
400INVALID_CLUSTER_IDValidationNo
401UNAUTHORIZEDAuthNo
403FORBIDDENAuthNo
403CLUSTER_ACCESS_DENIEDAuth (cluster ACL)No
404CLUSTER_NOT_FOUNDNotFoundNo
404NAMESPACE_NOT_FOUNDNotFoundNo
404WORKLOAD_NOT_FOUNDNotFoundNo
404NODE_NOT_FOUNDNotFoundNo
404NODE_GROUP_NOT_FOUNDNotFoundNo
404RECOMMENDATION_NOT_FOUNDNotFoundNo
404TEAM_NOT_FOUNDNotFoundNo
404DEPARTMENT_NOT_FOUNDNotFoundNo
410CURSOR_EXPIREDPaginationNo (restart)
422VALIDATION_ERRORValidationNo
422INVALID_COST_MODEValidationNo
429RATE_LIMITEDRate limitYes (with backoff)
500INTERNAL_ERRORServerYes (with backoff)
502UPSTREAM_UNAVAILABLEUpstream (Cost Explorer only)Yes (with backoff)
503RATE_LIMIT_UNAVAILABLERate-limit enforcement temporarily unavailableYes (with backoff)
503SERVICE_UNAVAILABLEService temporarily unavailableYes (with backoff)
504UPSTREAM_TIMEOUTUpstream timeout (Cost Explorer only)Yes (with backoff)

Six codes are retryable. The rest are non-transient programming or data errors; retrying them wastes quota.

Family breakdown

Auth (401, 403)

Three distinct codes, three distinct meanings. Branch on the code, not on the HTTP status.

CodeWhat it meansWhat to do
UNAUTHORIZEDThe Authorization header is missing, malformed, or carries an invalid/expired token.Recheck the Bearer header. Rotate the key if it was revoked.
FORBIDDENThe token is valid, but lacks the scope the endpoint requires (for example clusters:read).Mint a new key with the right scopes. The details block lists what the endpoint required.
CLUSTER_ACCESS_DENIEDThe token has the right scope at the org level, but is not allowed on the specific cluster you queried.Add the cluster to the key's allow-list, or use a different key.

A FORBIDDEN response carries the required scope inline:

json
1{
2  "data": null,
3  "meta": {
4    "request_id": "req_01J5K3V0Q7Y4XR8A2B3C5D7E9G",
5    "applied_at": "2026-05-21T10:30:00Z"
6  },
7  "error": {
8    "code": "FORBIDDEN",
9    "message": "API key lacks the required scope",
10    "details": [
11      { "reason": "missing_scope", "required": "clusters:read" }
12    ]
13  }
14}

A CLUSTER_ACCESS_DENIED response names the cluster:

json
1{
2  "data": null,
3  "meta": {
4    "request_id": "req_01J5K3V0Q7Y4XR8A2B3C5D7E9H",
5    "applied_at": "2026-05-21T10:30:00Z"
6  },
7  "error": {
8    "code": "CLUSTER_ACCESS_DENIED",
9    "message": "key is not authorized on this cluster",
10    "details": [
11      { "cluster_id": "c1a2b3c4-d5e6-7890-abcd-ef1234567890" }
12    ]
13  }
14}

Request shape (400)

Returned when the request is parseable but the inputs are malformed before any business logic runs. UUIDs that aren't UUIDs, unknown filter keys, cursors that fail base64 decode, limit=abc.

CodeWhen
BAD_REQUESTGeneric malformed input. Unknown query parameter, limit out of range, body fails to parse.
INVALID_CURSORThe ?cursor= value is not base64url-decodable or its decoded JSON is malformed. Restart pagination.
INVALID_CLUSTER_IDA path or query cluster ID is not a valid UUID. Different from CLUSTER_NOT_FOUND (which means the ID parsed but the cluster doesn't exist).
json
1{
2  "data": null,
3  "meta": {
4    "request_id": "req_01J5K3V0Q7Y4XR8A2B3C5D7E9J",
5    "applied_at": "2026-05-21T10:30:00Z"
6  },
7  "error": {
8    "code": "BAD_REQUEST",
9    "message": "unknown filter key",
10    "details": [
11      { "field": "frobnicate", "reason": "unknown_filter_key" }
12    ]
13  }
14}

Validation (422)

Returned when the input parses but is semantically wrong. Wrong enum value, conflicting flags, a string where a number was needed and parseable.

CodeWhen
VALIDATION_ERRORGeneral semantic error. details lists the field, the reason, and the allowed set when applicable.
INVALID_COST_MODETwo cases. First: ?cost_mode= carries a value that is not one of fully_loaded / workload_only. Second: the endpoint rejects ?cost_mode= entirely (clusters, nodes, recommendations, etc., see Cost Modes).
json
1{
2  "data": null,
3  "meta": {
4    "request_id": "req_01J5K3V0Q7Y4XR8A2B3C5D7E9K",
5    "applied_at": "2026-05-21T10:30:00Z"
6  },
7  "error": {
8    "code": "INVALID_COST_MODE",
9    "message": "cost_mode must be fully_loaded or workload_only",
10    "details": [
11      {
12        "field": "cost_mode",
13        "reason": "must_be_enum",
14        "allowed": ["fully_loaded", "workload_only"]
15      }
16    ]
17  }
18}

Not found (404)

Eight typed codes, one per resource family. This is intentional: branch on the code, not on a regex of the message. If you have a polymorphic ID resolver, switch over the code.

CodeReturned by
CLUSTER_NOT_FOUND/v1/clusters/{id} and any nested endpoint when the cluster does not exist in your tenant.
NAMESPACE_NOT_FOUND/v1/clusters/{id}/namespaces/{ns}.
WORKLOAD_NOT_FOUND/v1/workloads/{uid} and /v1/workloads/{uid}/pods.
NODE_NOT_FOUND/v1/nodes/{node_uid} (lookup by Kubernetes metadata.uid).
NODE_GROUP_NOT_FOUND/v1/clusters/{id}/node-groups/{name}.
RECOMMENDATION_NOT_FOUND/v1/recommendations/{id}.
TEAM_NOT_FOUND/v1/teams/{id} and assignment endpoints.
DEPARTMENT_NOT_FOUND/v1/departments/{id}.

A 404 means the ID was well-formed but no such resource exists in your tenant (or you can't see it under your key's scope). Don't retry a 404; the resource isn't coming back.

Pagination (410)

CodeWhendetails[0].reason
CURSOR_EXPIREDCursor older than the 24h TTL.expired
CURSOR_EXPIREDCursor's bound query parameters don't match the current request.query_changed

See Pagination for the cursor model and the 24-hour TTL. Restart the walk with no cursor.

Rate and availability (429, 502, 503, 504)

CodeHTTPWhenRetry?
RATE_LIMITED429Per-key request quota exceeded. The response carries a Retry-After header (seconds).Yes, sleep Retry-After then retry.
RATE_LIMIT_UNAVAILABLE503Rate-limit enforcement is temporarily unavailable. Retry with exponential backoff.Yes, exponential backoff.
SERVICE_UNAVAILABLE503A part of Kubeadapt's service is temporarily unavailable.Yes, exponential backoff.
UPSTREAM_UNAVAILABLE502An upstream service backing POST /v1/cost-explorer/query returned 5xx or was unreachable.Yes, exponential backoff.
UPSTREAM_TIMEOUT504An upstream call backing POST /v1/cost-explorer/query exceeded the 60-second deadline.Yes, exponential backoff.
json
1{
2  "data": null,
3  "meta": {
4    "request_id": "req_01J5K3V0Q7Y4XR8A2B3C5D7E9L",
5    "applied_at": "2026-05-21T10:30:00Z"
6  },
7  "error": {
8    "code": "RATE_LIMITED",
9    "message": "request quota exceeded for this key",
10    "details": [
11      { "reason": "per_key_quota_exceeded" }
12    ]
13  }
14}

The Retry-After header is the source of truth; honor it before adding your own jitter on top.

Server (500)

CodeWhen
INTERNAL_ERRORUnhandled server error. Carries a request_id. Report persistent occurrences to support@kubeadapt.io with the request_id.

A 500 is retryable once or twice with backoff. Escalate to support if it persists.

Retry strategy

Six codes are retryable: RATE_LIMITED, RATE_LIMIT_UNAVAILABLE, SERVICE_UNAVAILABLE, UPSTREAM_UNAVAILABLE, UPSTREAM_TIMEOUT, and INTERNAL_ERROR. Every other code in the catalog is non-retryable.

Recommended behavior:

  1. Honor Retry-After. On 429 RATE_LIMITED, the header carries the exact wait in seconds. Sleep at least that long before retrying.
  2. Use exponential backoff with jitter. For the other retryable codes, double the delay between attempts (e.g., 1s → 2s → 4s) and add randomized jitter to avoid synchronized retry storms.
  3. Cap attempts at 3-5. If the error persists past that, surface it; the issue is no longer transient.
  4. Never retry non-retryable codes. 400, 401, 403, 404, 410, 422 will not resolve by retrying. Repeating an authentication failure can lead to the key being blocked.

The details payload shape

error.details is an optional array. Each entry uses three documented keys plus free-form inline keys for case-specific data.

KeyTypeWhen present
fieldstringThe name of the parameter or body field that triggered the error (cost_mode, limit, frobnicate).
reasonstringMachine-readable reason code (must_be_enum, expired, query_changed, unknown_filter_key, not_supported_on_this_endpoint).
allowedarray of stringsThe allowed values when the field is enum-typed.

Some details carry inline custom keys at the same level (no nested extra block):

  • FORBIDDEN → required (the missing scope, e.g. "clusters:read").
  • CLUSTER_ACCESS_DENIED → cluster_id (the cluster the key cannot reach).

Four worked details payloads:

json
1// 1. Validation, enum-typed field.
2{
3  "field": "cost_mode",
4  "reason": "must_be_enum",
5  "allowed": ["fully_loaded", "workload_only"]
6}
7
8// 2. Cost mode rejected on a cluster-family endpoint.
9{
10  "field": "cost_mode",
11  "reason": "not_supported_on_this_endpoint"
12}
13
14// 3. Unknown filter key on a list endpoint.
15{
16  "field": "frobnicate",
17  "reason": "unknown_filter_key"
18}
19
20// 4. Cursor query mismatch.
21{
22  "reason": "query_changed"
23}

Pre-flight checklist

Before shipping a client, verify it:

  • Branches on error.code, never on error.message.
  • Treats unknown codes as non-retryable.
  • Reads meta.request_id and logs it on every failed call.
  • Implements the retry strategy above with Retry-After support on 429.
  • Does NOT retry 400, 401, 403, 404, 410, or 422.

See also

  • API Overview, the envelope shape and the full endpoint index.
  • Pagination, where INVALID_CURSOR and CURSOR_EXPIRED come from.
  • Authentication, the Bearer-token model behind UNAUTHORIZED, FORBIDDEN, and CLUSTER_ACCESS_DENIED.
  • Cost Modes, the contract behind INVALID_COST_MODE on the cluster, node, and recommendation families.
  • Permission Scopes, the catalog of scopes that drives 403 responses.

Related

  • API Overview
  • Pagination
  • Authentication
PreviousPaginationAPI ReferenceNextCost ModesAPI Reference

On this page

  • The error envelope
  • Error code catalog
  • Family breakdown
  • Auth (401, 403)
  • Request shape (400)
  • Validation (422)
  • Not found (404)
  • Pagination (410)
  • Rate and availability (429, 502, 503, 504)
  • Server (500)
  • Retry strategy
  • The details payload shape
  • Pre-flight checklist
  • See also
Edit this page
Kubeadapt

Kubernetes FinOps platform. Cost visibility, rightsizing, and capacity planning that pays for itself in 30 days.

Product

  • Cost Monitoring
  • Cost Attribution
  • Workload Rightsizing
  • Recommendations
  • Smart Alerting
  • Best Practices
  • Network Cross-AZ

Resources

  • Documentation
  • Status Page
  • Feature Requests

Company

  • About Us
  • Security
  • Careers
  • Contact

© 2026 Kubeadapt. All rights reserved.

PrivacyTermsSecurity