Errors
The Elastio API uses standard HTTP status codes. Codes in the 2xx range mean success, 4xx means something is wrong with the request, and 5xx means something went wrong on Elastio’s side. Error responses have a JSON body that explains what happened.
The error envelope
Section titled “The error envelope”Most operations return errors in the gateway’s ErrorResponse envelope:
{ "status": 404, "error": "Not Found", "message": "Asset 6c1f… was not found"}| Field | Type | Description |
|---|---|---|
status |
integer | The HTTP status code, repeated in the body (400–599). |
error |
string | A short name for the status, such as Not Found. |
message |
string | A human-readable explanation. Log it, but don’t parse it: the wording can change. |
cached |
boolean | Optional. Whether the response was served from cache. |
Service-specific error bodies
Section titled “Service-specific error bodies”Some services behind the gateway return their own error shape. Branch on the HTTP status code first, and treat the body as diagnostic detail:
- Flat with a code:
{ "error": "…", "code": "…", "details": "…" }, used by many shared-services operations.codeis machine-readable, for exampleALREADY_EXISTSorNOT_FOUND. - Nested:
{ "error": { "code": "…", "message": "…" } }, used by the Slack and EQL operations. - Plain text: some Cloud Configuration operations under
/gateway/ingestion/v1/posturereturntext/plainerror bodies.
Each operation’s possible responses and their schemas are listed in the API reference.
Status codes
Section titled “Status codes”| Code | Meaning | What to do |
|---|---|---|
400 Bad Request |
The request is malformed: a missing or invalid parameter, an unknown enum value, or a body that doesn’t match the schema. | Fix the request. The message usually names the problem. Don’t retry unchanged. |
401 Unauthorized |
The token is missing, malformed, expired or revoked. | Check the Authorization: Bearer header and the token’s expiry. See Authentication. |
403 Forbidden |
The token is valid but not allowed to do this: its grants don’t cover the operation, or the operation needs a tenant admin. | Add the needed scope or write access, or use an appropriately privileged token. |
404 Not Found |
The resource doesn’t exist in your tenant, or the feature is not enabled for your tenant. | Check the ID and path. |
409 Conflict |
The request conflicts with the current state, such as creating something that already exists or an action that is invalid for the resource’s state. | Re-read the resource and decide what to do. |
410 Gone |
The operation has been retired. | Move to the replacement listed on Deprecations. |
412 Precondition Failed |
A precondition for the write was not met, for example a stale version. | Re-read the resource, then retry with current data if it still makes sense. |
422 Unprocessable Entity |
The request is well-formed but can’t be processed. Documented on three operations: POST /gateway/shared-services/v1/documents/pdf, POST /gateway/reporting/v2/schedules and POST /gateway/reporting/v1/eql/draft. |
Fix the content of the request. |
429 Too Many Requests |
A capacity limit was hit. Documented on a small set of operations: the EQL operations under /gateway/reporting/v1/eql (admission or queue budget exceeded), VirusTotal file lookups (VirusTotal rate limit), PDF rendering (renderer busy) and POST /gateway/api/v1/aws/inventory-scans. |
Wait and retry with backoff. |
500 Internal Server Error |
An unexpected error on Elastio’s side. | Retry with backoff. If it persists, contact support. |
501 Not Implemented |
The action isn’t available for this resource, for example some finding dispositions. | Don’t retry. |
502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout |
A backend service or a downstream provider is unavailable or slow. | Retry with backoff. |
The spec does not document rate limits or a Retry-After header for the API as a whole. Write clients that handle 429 wherever it appears.
Retrying
Section titled “Retrying”Retry only when a later attempt can succeed:
- Retry network errors (connection reset, DNS failure, timeout),
429,500,502,503and504. - Don’t retry other
4xxresponses unchanged. They will fail the same way until you fix the request, the token or the resource state. - Back off exponentially with jitter, for example 1, 2, 4, 8 seconds plus a random fraction, and cap the number of attempts.
- Be careful retrying writes. If a
POSTtimed out, it may have succeeded. Read the resource before you send it again, or use the operation’s version check where it has one.
# --retry covers timeouts and 408, 429, 500, 502, 503, 504 responses.curl --retry 5 --retry-delay 0 --retry-max-time 120 --fail-with-body \ "https://acme.app.elastio.com/gateway/api/v2/incidents?status=open" \ -H "Authorization: Bearer $ELASTIO_TOKEN"import os, random, time, requests
BASE = "https://acme.app.elastio.com"HEADERS = {"Authorization": f"Bearer {os.environ['ELASTIO_TOKEN']}"}RETRYABLE = {429, 500, 502, 503, 504}
def get(path, params=None, attempts=5): for attempt in range(attempts): try: resp = requests.get(f"{BASE}{path}", headers=HEADERS, params=params, timeout=30) except (requests.ConnectionError, requests.Timeout): if attempt == attempts - 1: raise else: if resp.status_code not in RETRYABLE or attempt == attempts - 1: resp.raise_for_status() return resp.json() time.sleep(2 ** attempt + random.random())
incidents = get("/gateway/api/v2/incidents", {"status": "open"})$Base = "https://acme.app.elastio.com"$Headers = @{ Authorization = "Bearer $env:ELASTIO_TOKEN" }$Retryable = 429, 500, 502, 503, 504
function Invoke-ElastioGet([string]$Path, [int]$Attempts = 5) { for ($i = 0; $i -lt $Attempts; $i++) { try { return Invoke-RestMethod -Uri "$Base$Path" -Headers $Headers } catch { $status = $_.Exception.Response.StatusCode.value__ $isNetwork = -not $status if ((-not $isNetwork -and $status -notin $Retryable) -or $i -eq $Attempts - 1) { throw } Start-Sleep -Milliseconds ([math]::Pow(2, $i) * 1000 + (Get-Random -Maximum 1000)) } }}
Invoke-ElastioGet "/gateway/api/v2/incidents?status=open"Handling errors in code
Section titled “Handling errors in code”# --fail-with-body exits non-zero on 4xx/5xx but still prints the error body.curl --fail-with-body "https://acme.app.elastio.com/gateway/api/v2/incidents/00000000-0000-0000-0000-000000000000" \ -H "Authorization: Bearer $ELASTIO_TOKEN"resp = requests.get(f"{BASE}/gateway/api/v2/incidents/00000000-0000-0000-0000-000000000000", headers=HEADERS, timeout=30)if not resp.ok: try: body = resp.json() except ValueError: body = resp.text print(resp.status_code, body)try { Invoke-RestMethod -Uri "$Base/gateway/api/v2/incidents/00000000-0000-0000-0000-000000000000" -Headers $Headers} catch { $_.Exception.Response.StatusCode.value__ $_.ErrorDetails.Message}Errors from the API console relay
Section titled “Errors from the API console relay”When you send requests from the API console, the portal’s relay can reject a request before it reaches your tenant. Relay errors use their own body, with error always set to Console proxy error:
{ "status": 403, "error": "Console proxy error", "message": "The console can only call Elastio tenants (.elastio.com, .elastio.us). Check the tenant in the server URL."}| Status | Cause |
|---|---|
400 |
The target URL is missing, invalid, not HTTPS, uses a non-default port, or contains credentials. |
403 |
The target host is not an allowed Elastio host. Check the tenant. |
413 |
The request body is larger than 5 MB. |
502 |
The relay could not reach the tenant. |
504 |
The tenant did not respond within 60 seconds. |
Any other error in the console comes from your tenant and follows the formats above.