Error Reference
Complete reference for Wocha API error codes and handling patterns
Customer API errors return a consistent JSON body:
Use the error code for programmatic handling. The message field is human-readable and may change without notice.
Validation errors may also include an errors object mapping field names to message arrays. Rate-limited responses include retry_after (seconds).
API errors vs SDK auth errors
Two separate error systems cover different layers of the stack:
| Layer | Package | Error type | HTTP status | docs_url |
|---|---|---|---|---|
| Customer API | @wocha/sdk, wocha, wocha-go | Typed API errors (WochaAuthenticationError, PermissionError, …) | Real HTTP status (401, 404, 429, …) | Yes — links to pages below |
| End-user auth | @wocha/react, @wocha/nextjs | WochaAuthError with code property | N/A (thrown client-side during OAuth) | Documented under SDK auth errors |
Management API errors include request_id for support. SDK auth errors occur during login, callback, token refresh, and ID token validation — handle them in UI routes and auth handlers, not in Customer API middleware.
Handling errors in your application
All three Management SDKs map HTTP status codes to typed exceptions. Use errorCode / Code to branch on specific codes within each type.
TypeScript (@wocha/sdk)
Python (wocha)
Go (github.com/wocha-dev/wocha-go)
Authentication and keys
These errors indicate the caller could not be authenticated. Do not retry without fixing credentials.
| Code | HTTP | Description | Common causes | Resolution |
|---|---|---|---|---|
unauthorized | 401 | Missing or malformed Authorization header | No Bearer prefix; header omitted entirely | Add Authorization: Bearer wocha_mgmt_… or a valid OAuth access token |
invalid_key | 401 | Management API key rejected | Wrong key, wrong tenant, revoked or expired key, rotation grace period ended | Create a new key in Console; verify WOCHA_TENANT matches the key's tenant |
expired_key | 401 | Key past its expires_at | Key created with expiry; rotation grace expired | Rotate or create a replacement key; update secrets in your deployment |
key_revoked | 401 | Key explicitly revoked | Key deleted in Console; security rotation | Create a new key; audit logs show key_revoked in metadata |
token_inactive | 401 | OAuth bearer token inactive or expired | Access token expired; token revoked in Hydra | Refresh the token or re-run the OAuth flow |
auth_failed | 401 | Authentication subsystem error | Database unavailable during key lookup; unexpected failure | Retry once; if persistent, check platform status and contact support with request_id |
introspection_failed | 401 | OAuth token introspection failed | Hydra unreachable; malformed bearer token | Verify token format; check Hydra connectivity for self-hosted deployments |
Example — authentication errors
TypeScript
Python
Go
Authorisation, scopes, and quotas
These errors mean the caller is authenticated but not permitted to perform the action. Do not retry without changing scopes, plan tier, or permissions.
| Code | HTTP | Description | Common causes | Resolution |
|---|---|---|---|---|
permission_denied | 403 | Generic permission failure | SpiceDB check denied; actor lacks resource access | Verify relationships via permissions.check() or Console |
forbidden | 403 | Alias for insufficient access | Same as permission_denied | See permission_denied |
insufficient_scope | 403 | API key or token missing required scope | Key scoped to users:read calling users:write | Add scope in Console → Settings → API keys |
tenant_inactive | 403 | Tenant suspended or deactivated | Billing issue; manual suspension | Contact support or resolve billing |
feature_not_available | 403 | Feature not on current plan | Audit log export on Free tier; permission schema on Starter | Upgrade plan or use an alternative endpoint |
feature_unavailable | 403 | Alias for plan-gated features | Entitlement check failed | Upgrade plan |
quota_exceeded | 403 | Plan resource quota reached | Too many applications, webhooks, or MAU cap | Review usage in Console → Billing; upgrade or delete unused resources |
Example — authorisation errors
TypeScript
Python
Go
Validation and client errors
These errors indicate a problem with the request itself. Fix the payload before retrying.
| Code | HTTP | Description | Common causes | Resolution |
|---|---|---|---|---|
validation_error | 400 / 422 | Field-level validation failed | Invalid slug format; missing required field; value out of range | Read errors / fieldErrors; compare against OpenAPI spec |
invalid_request | 400 | Malformed request | Invalid JSON body; unsupported content type | Verify JSON syntax and Content-Type: application/json |
missing_tenant | 400 | Tenant context unresolved | OAuth token without tenant claim; missing X-Tenant-ID | Pass X-Tenant-ID header or use a tenant-scoped token |
billing_not_configured | 400 | Billing not set up | Calling billing portal before Stripe configuration | Complete billing setup in Console |
conflict | 409 | Resource already exists | Duplicate slug, email, or product identifier | Use a unique identifier or fetch the existing resource |
not_found | 404 | Resource does not exist | Wrong ID; resource deleted; wrong tenant | Verify ID from a recent list() response; handle 404 gracefully in read paths |
Example — validation and client errors
TypeScript
Python
Go
Rate limiting
| Code | HTTP | Description | Common causes | Resolution |
|---|---|---|---|---|
rate_limit_exceeded | 429 | Per-minute request limit exceeded (canonical code) | Polling loops; bulk scripts without delays; shared key across services | Respect Retry-After; enable SDK auto-retry; reduce request volume |
rate_limited | 429 | Legacy alias for rate limiting | Same as rate_limit_exceeded | Same as above |
When rate limited, responses include:
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed in the window |
X-RateLimit-Remaining | Requests remaining in the current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
Retry-After | Seconds to wait before retrying (429 only) |
Rate limit tiers
| Tier | Rate limit |
|---|---|
| Free | 100 req/min |
| Starter | 500 req/min |
| Pro | 2,000 req/min |
| Enterprise | 10,000 req/min |
Example — rate limiting
TypeScript
Python
Go
Note: The SDK does not retry
quota_exceeded(403) — only transient 429/5xx responses.
Server and upstream errors
These errors indicate a platform or dependency failure. Safe to retry with exponential backoff.
| Code | HTTP | Description | Common causes | Resolution |
|---|---|---|---|---|
internal_error | 500 | Unhandled server exception | Unexpected bug; database timeout | Retry with backoff; contact support with request_id if persistent |
server_error | 500 | Generic server failure | Same as internal_error | Same as above |
upstream_error | 502 | Upstream dependency failed | Kratos, Hydra, or SpiceDB returned an error | Retry with backoff; check self-hosted service health |
service_unavailable | 503 | Required service not configured or down | SpiceDB not configured; billing service offline | Verify service configuration; check platform status |
unknown_error | varies | Unrecognised error code | New API error not yet in SDK; proxy interference | Log full response; check SDK version; contact support |
Example — server errors
TypeScript
Python
Go
SDK auth errors
These are thrown by @wocha/react and @wocha/nextjs during end-user authentication — not returned as Customer API JSON.
| Code | Description | Common causes | Resolution |
|---|---|---|---|
state_mismatch | OAuth state parameter mismatch | CSRF check failed; session cookie lost; multiple tabs | Restart login flow; ensure cookies persist across redirect |
token_exchange_failed | Authorization code exchange failed | Invalid code; redirect URI mismatch; expired code | Verify redirect URI in Console; retry login |
token_refresh_failed | Refresh token rejected | Refresh token expired or revoked | Force re-login |
missing_verifier | PKCE code verifier missing | Session storage cleared between redirect steps | Ensure PKCE verifier stored in same session/cookie |
network_error | Network failure during auth | Offline client; DNS failure; timeout | Retry; check connectivity |
auth_failed | Generic auth failure | Provider error; misconfigured client | Check Console OAuth app settings |
invalid_id_token | ID token validation failed | Wrong issuer, audience, expiry, or nonce | Verify client ID and issuer URL |
Next.js also defines: id_token_issuer_mismatch, id_token_audience_mismatch, id_token_expired, oidc_discovery_failed, jwks_fetch_failed, invalid_nonce — see invalid_id_token and network_error.
Example — SDK auth errors (React / Next.js)
WochaValidationError.fieldErrors
When the API returns validation_error, the JSON body may include an errors object:
The TypeScript SDK maps this to WochaValidationError.fieldErrors:
Properties:
fieldErrors—Record<string, string[]>|undefined. Keys are API field names; values are one or more human-readable messages per field.statusCode—400or422depending on the route.errorCode— always"validation_error"for this class.
Use fieldErrors for inline form validation. Use message for a generic summary banner.
Quick reference index
| Category | Codes |
|---|---|
| Authentication | unauthorized, invalid_key, expired_key, key_revoked, token_inactive, auth_failed, introspection_failed |
| Authorisation | permission_denied, forbidden, insufficient_scope, tenant_inactive, feature_not_available, feature_unavailable, quota_exceeded |
| Client | validation_error, invalid_request, missing_tenant, billing_not_configured, conflict, not_found |
| Rate limiting | rate_limit_exceeded, rate_limited |
| Server | internal_error, server_error, upstream_error, service_unavailable, unknown_error |
| SDK auth | state_mismatch, token_exchange_failed, token_refresh_failed, missing_verifier, network_error, auth_failed, invalid_id_token |
Individual error pages with additional context:
- authentication-error · permission-denied · rate-limit-exceeded
- validation-error · webhook-verification · token-expired
- invalid-configuration
- invalid_key · expired_key · key_revoked
- auth_failed · not_found · conflict
- internal_error · server_error · service_unavailable
- unknown_error · network_error