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

Standard conventions for ScaiDNS error responses. This page covers the shape, the status codes, and how to interpret the most common cases. See Error Codes for the exhaustive reference.

Error response shape#

All error responses use this shape:

json
1
2
3
{
  "detail": "A human-readable message describing what went wrong"
}

For some structured errors (validation failures, conflicts), detail is an object instead of a string:

json
1
2
3
4
5
6
7
{
  "detail": {
    "message": "Record conflicts with existing entries",
    "field": "content",
    "conflicts": ["a1b2c3d4-..."]
  }
}

Pydantic validation errors from FastAPI follow the standard pattern:

json
1
2
3
4
5
6
7
8
9
{
  "detail": [
    {
      "loc": ["body", "name"],
      "msg": "field required",
      "type": "value_error.missing"
    }
  ]
}

HTTP status codes#

ScaiDNS uses standard HTTP status codes.

2xx — Success#

Code Meaning
200 OK GET, PATCH, POST with a response body
201 Created POST creating a resource (sometimes — usually just 200 with body)
204 No Content DELETE or write that returns no body

4xx — Caller error#

Code Meaning Typical cause
400 Bad Request Malformed request or invalid input Missing field, bad enum value, invalid record content
401 Unauthorized Missing or invalid credentials No X-API-Key/Authorization header; expired token; invalid signature
403 Forbidden Authenticated but not allowed Insufficient permissions, IP whitelist mismatch, cross-tenant attempt
404 Not Found Resource doesn't exist (or you can't see it) Wrong UUID, soft-deleted without ?include_deleted, tenant scoping
409 Conflict Write conflicts with current state Duplicate domain name, record collision (CNAME + A), role already assigned
422 Unprocessable Entity Schema validation failed Pydantic validation errors; usually a typed detail array
429 Too Many Requests Rate limit exceeded API key or global rate limit hit; check Retry-After header

5xx — Server error#

Code Meaning What to do
500 Internal Server Error Unhandled exception Retry with backoff; check platform health; if it persists, contact support
502 Bad Gateway PowerDNS or ScaiKey unreachable Usually transient; retry
503 Service Unavailable Maintenance or overload Retry with backoff
504 Gateway Timeout PowerDNS or ScaiKey slow Retry; check dashboards

Common patterns#

Validation failures#

On POST and PATCH with invalid input, expect 422:

json
1
2
3
4
5
{
  "detail": [
    {"loc": ["body", "content"], "msg": "invalid IP address", "type": "value_error"}
  ]
}

Fix the input and retry.

Tenant mismatches#

Trying to read a resource from another tenant returns 404, not 403. This is intentional — leaking existence-by-status would let a caller enumerate resources across tenant boundaries.

Permission failures#

Trying to take an action your role doesn't permit returns 403:

json
1
{"detail": "records:write permission required on this domain"}

Check effective permissions: GET /api/v1/roles/users/{your_id}/permissions?domain_id={domain}.

Soft delete / not-found#

A soft-deleted domain returns 404 on normal reads. To restore:

bash
1
2
curl -X POST https://scaidns.scailabs.ai/api/v1/domains/$DOMAIN_ID/restore \
  -H "X-API-Key: $SCAIDNS_API_KEY"

Record conflicts#

DNS has its own rules about which records can coexist:

  • CNAME and A/AAAA can't share a name.
  • DNAME and any other record can't share a name.
  • NS at the zone apex requires at least one to remain present.

Violations return 409 with specifics:

json
1
2
3
4
5
6
7
{
  "detail": {
    "message": "Cannot create A record: CNAME exists at www.example.com",
    "field": "type",
    "conflicts": ["record-uuid-of-the-cname"]
  }
}

DNSSEC errors#

Particular cases to watch:

  • Enabling when already enabled. 409 — disable first or use rotate.
  • Confirming DS that doesn't match KSK. 400 — check the key_tag and digest.
  • Disabling with DS still published. Allowed, but resolvers will see broken DNSSEC. Remove DS at registrar first.

Rate limiting headers#

When you approach a rate limit, responses include:

text
1
2
3
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 42
X-RateLimit-Reset: 1714567890

When exceeded (429):

text
1
Retry-After: 60

The Retry-After value is either seconds or an HTTP date.

Idempotency#

Most state changes are not idempotent at the HTTP level. Retrying a failed POST /records may create a duplicate if the first attempt partially succeeded. The server protects against this with:

  • Unique constraints on (domain_id, name, type, content) — duplicate records return 409.
  • Bulk operations default to atomic (all-or-nothing) mode; partial success requires opting in with continue_on_error.

If you need client-side idempotency, keep a record of the last successful create and check for its existence before retrying.

Audit trail#

Every write goes to the audit log. If an operation succeeds but the response was lost in transit, you can inspect GET /api/v1/admin/audit-logs to confirm the mutation happened before retrying.

What's next#

Updated 2026-05-17 02:38:19 View source (.md) rev 1