---
title: Error Codes
path: reference/error-codes
status: published
---

# Error Codes

Complete stable vocabulary of error codes. Branch on `error.code`, not on HTTP status.

For the error envelope and retry semantics, see [Errors](../core-concepts/errors).

## Envelope

```json
{
  "error": {
    "code": "secret_not_found",
    "message": "Secret 'environments/production/missing' not found",
    "details": {"path": "environments/production/missing"},
    "request_id": "req_abc"
  }
}
```

## Authentication / authorization

| HTTP | Code | Meaning |
|------|------|---------|
| 401 | `authentication_required` | Missing Authorization header |
| 401 | `token_expired` | Token `exp` passed |
| 401 | `token_invalid` | Signature failed |
| 401 | `token_revoked` | Explicitly revoked |
| 403 | `access_denied` | No matching policy rule |
| 403 | `insufficient_scope` | Token scope too narrow |
| 403 | `mfa_required` | Rule requires fresh MFA |
| 403 | `ip_not_allowed` | IP outside allowed ranges |
| 403 | `time_window_violation` | Outside time window |
| 403 | `tenant_access_denied` | Cross-tenant without partner admin |
| 403 | `forbidden` | Generic authorization failure |

## Validation

| HTTP | Code | Meaning |
|------|------|---------|
| 400 | `invalid_request` | Malformed JSON or shape |
| 400 | `invalid_path` | Path format violation |
| 400 | `invalid_secret_type` | Unknown secret_type |
| 400 | `invalid_duration` | Duration string didn't parse |
| 400 | `invalid_cursor` | Cursor expired or malformed |
| 400 | `invalid_policy` | Policy rules/bindings invalid |
| 400 | `invalid_glob` | Path pattern not a valid glob |
| 400 | `invalid_role` | Role name invalid |
| 400 | `invalid_config` | Engine/backend config rejected |
| 422 | `validation_error` | Field-level validation; see `details.fields` |

## Not found

| HTTP | Code |
|------|------|
| 404 | `secret_not_found` |
| 404 | `version_not_found` |
| 404 | `policy_not_found` |
| 404 | `binding_not_found` |
| 404 | `webhook_not_found` |
| 404 | `subscription_not_found` |
| 404 | `rotation_policy_not_found` |
| 404 | `secret_policy_not_found` |
| 404 | `ca_not_found` |
| 404 | `certificate_not_found` |
| 404 | `csr_not_found` |
| 404 | `trust_anchor_not_found` |
| 404 | `acme_account_not_found` |
| 404 | `acme_order_not_found` |
| 404 | `dns_provider_not_found` |
| 404 | `engine_not_found` |
| 404 | `role_not_found` |
| 404 | `lease_not_found` |
| 404 | `tenant_not_found` |
| 404 | `identity_not_found` |
| 404 | `federation_backend_not_found` |
| 404 | `export_not_found` |

## Conflict / state

| HTTP | Code | Meaning |
|------|------|---------|
| 409 | `secret_exists` | Soft-deleted path in retention |
| 409 | `version_conflict` | Concurrent modification |
| 409 | `policy_in_use` | Policy has bindings |
| 409 | `engine_in_use` | Engine has leases |
| 409 | `ca_has_certificates` | CA has issued certs |
| 409 | `name_conflict` | Name already taken |
| 409 | `binding_conflict` | Identity already bound |
| 410 | `secret_expired` | `expires_at` passed |
| 410 | `lease_expired` | Lease already expired |

## Rate limit / quota / availability

| HTTP | Code | Meaning |
|------|------|---------|
| 429 | `rate_limited` | Caller-specific rate limit |
| 429 | `quota_exceeded` | Hard tenant quota |
| 503 | `service_unavailable` | Transient dependency outage |
| 503 | `feature_disabled` | Feature gated off at deploy |
| 503 | `maintenance_mode` | Operator-scheduled downtime |

## Server / backend

| HTTP | Code | Meaning |
|------|------|---------|
| 500 | `internal_error` | Unexpected server error |
| 500 | `encryption_error` | KMS interaction failed |
| 500 | `storage_error` | Database or object store error |
| 502 | `backend_error` | Federated backend or ACME upstream failed |
| 502 | `backend_rate_limited` | Federated/ACME backend rate-limited us |
| 504 | `backend_timeout` | Federated backend timeout |

## Dynamic / PKI specific

| HTTP | Code | Meaning |
|------|------|---------|
| 400 | `ttl_exceeds_max` | Requested TTL > role max_ttl |
| 400 | `domain_not_allowed` | CN/SAN not in role's allowed_domains |
| 400 | `key_type_mismatch` | CSR key doesn't match role requirement |
| 400 | `csr_invalid` | CSR failed to parse |
| 409 | `csr_already_processed` | CSR already signed or rejected |
| 502 | `acme_order_failed` | ACME upstream rejected order |
| 502 | `engine_unreachable` | Can't reach backend system |

## Webhook delivery

| HTTP (reported) | Code | Meaning |
|-----------------|------|---------|
| 400 | `webhook_url_invalid` | URL can't be used (private IP blocked, etc.) |
| 502 | `webhook_delivery_failed` | Non-2xx from destination |
| 504 | `webhook_delivery_timeout` | > 10s response time |

## Headers

On every response:

- `X-Request-ID: req_abc` — include in support tickets.
- `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`.

On 429:

- `Retry-After: 45` — seconds.

## Related

- [Errors](../core-concepts/errors) — semantics.
- [Rate Limiting](../advanced/rate-limiting) — per-category caps.
