Platform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Models Tools & Services
Solutions
Organisations Developers Internet Service Providers Managed Service Providers AI-in-a-Box
Resources
Support Documentation Blog Downloads
Company
About Research Careers Investment Opportunities Contact
Log in

Errors

The HTTP status code carries the primary signal. The response body shape depends on the kind of failure: regular errors return {"detail": "<message>"}, request-validation failures return FastAPI's structured detail array.

Regular errors#

Anything raised through the normal exception path — auth rejected, permission denied, resource not found, conflict, rate-limit, server error — comes back as:

json
1
{"detail": "Permission denied: WRITE on file/fil_01J3M"}

The HTTP status code tells you the class. The detail string is human-readable; surface it in UIs or logs but don't parse it.

Status Class When
400 Bad request Malformed path or input that's not a schema-level validation failure
401 Unauthorized Missing, invalid, expired, or wrong-issuer token
403 Forbidden Authenticated but lacking permission; user suspended; ownership-only action
404 Not found Resource doesn't exist, or isn't visible to the caller — we don't distinguish
409 Conflict Name collision, version mismatch, already-in-state operations (e.g., invitation already used)
410 Gone Soft-deleted resource; expired invitation, link, or upload session
413 Payload too large Upload exceeds tenant's per-file size cap
422 Unprocessable Pydantic schema validation failed — see next section
429 Rate limited Honor Retry-After header where present
500 Internal Unexpected server failure
502 Bad gateway Upstream service unreachable (ScaiKey, S3, Weaviate, SMB/SharePoint connector source)
503 Service unavailable Dependency drained or degraded; readiness probe failing
504 Gateway timeout Upstream timed out
507 Insufficient storage A quota would be exceeded — share, user, group, tenant, or partner level

Validation errors (422)#

When a request body fails Pydantic validation, FastAPI returns its standard structured error with a list of issues. detail is an array, not a string:

json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{
  "detail": [
    {
      "loc": ["body", "role"],
      "msg": "Input should be 'owner', 'admin', 'contributor' or 'reader'",
      "type": "enum"
    },
    {
      "loc": ["body", "expires_in_days"],
      "msg": "Input should be less than or equal to 365",
      "type": "less_than_equal"
    }
  ]
}

Each entry has:

  • loc — a path from the request root to the failing field (e.g., ["body", "role"], ["query", "limit"])
  • msg — human-readable description
  • type — the validator kind that fired (enum, missing, string_too_short, etc.)

Parse this on the client side to render field-level error messages.

Detecting the shape#

detail can be either a string or an array. The two shapes never appear together. Practical client code:

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import httpx

resp = httpx.post(url, json=payload, headers={"Authorization": f"Bearer {token}"})
if resp.status_code >= 400:
    body = resp.json()
    detail = body.get("detail")
    if isinstance(detail, list):
        # Validation errors — show field-by-field
        for issue in detail:
            field = ".".join(str(x) for x in issue["loc"])
            print(f"{field}: {issue['msg']}")
    else:
        # Regular error — single message
        print(f"HTTP {resp.status_code}: {detail}")
typescript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
const resp = await fetch(url, { method: "POST", body: JSON.stringify(payload), headers });
if (!resp.ok) {
  const body = await resp.json();
  if (Array.isArray(body.detail)) {
    for (const issue of body.detail) {
      const field = issue.loc.join(".");
      console.error(`${field}: ${issue.msg}`);
    }
  } else {
    console.error(`HTTP ${resp.status}: ${body.detail}`);
  }
}

Retry strategy#

Status Retry? Notes
401 No Refresh the token if AUTH_TOKEN_EXPIRED; otherwise fix credentials
403 No User lacks permission — no amount of retrying will fix it
404 No Resource doesn't exist
409 Maybe Re-read current state, resolve, retry once. For sync conflicts, use the conflict-resolution API
410 No Restore the resource or create a new one
413 No Use resumable uploads for large files
422 No Fix the request body
429 Yes Honor Retry-After; exponential backoff with jitter
500 Once Transient. Don't retry destructive operations without idempotency
502, 503, 504 Yes Upstream issue; backoff and retry
507 No Quota exceeded; free space or raise the quota

Streaming errors#

For file downloads, an error after response headers are sent produces a truncated body — your client must verify the full Content-Length (or the file's checksum_sha256) before treating a stream as complete.

For WebSocket connections, errors typically arrive as connection-close codes. The custom close codes ScaiDrive uses:

Code Meaning
4401 Auth token invalid or missing
4403 User suspended or no permission to subscribe
4429 Connection limit hit (per-token or per-tenant)

Standard WebSocket codes (1000, 1001, 1006, ...) carry their usual meaning.

Known gaps#

Two things this API doesn't have yet, worth knowing about:

  • No machine-readable error codes. The detail string is the only error identifier. If you're building robust error handling, branch on HTTP status code + the operation you were performing, not on substring matching the message.
  • No request ID propagation. Responses don't include an X-Request-Id header or a body-level request identifier today. When filing support tickets, include the request timestamp, status code, full URL, and the error message — that's the highest-fidelity correlation we can offer right now.

Both are tracked as planned improvements; this page will be updated when they ship.

What's next#

Updated 2026-05-18 15:04:12 View source (.md) rev 2