Skip to main content
Driftstack DRIFTSTACK

Error codes

Every error response from Driftstack is RFC 7807 application/problem+json. The stable identifier is the type URI; dispatch your client logic on type, not on the HTTP status or the human-readable title / detail.

Response shape

HTTP/1.1 429 Too Many Requests
Retry-After: 12
Content-Type: application/problem+json

{
  "type": "https://errors.driftstack.dev/rate-limited",
  "title": "Too Many Requests",
  "status": 429,
  "detail": "Rate limit for \"sessions:create\" exceeded for tier \"solo_manual\".",
  "retry_after_seconds": 12
}

Required fields: type, title, status. Optional: detail (human message), instance (request id when present), and problem-specific extensions (flat keys on the top-level, per RFC 7807 §3.2). Always check type, not the HTTP status alone — the same status can correspond to multiple types (e.g. 429 covers both rate-limited and concurrency-limit; 401 covers six distinct types).

Reference

The full URI is https://errors.driftstack.dev/<slug> where <slug> is the value in the first column. Slugs are stable; they will not be renamed without a deprecation window.

Type slug HTTP When Action
bad-request 400 Generic invariant violation (e.g. capacity ≤ 0, malformed URL). Read `detail`; the field at fault is named.
validation-failed 400 Request body / params failed Zod schema validation. Fix the field listed in the response detail.
unauthorized 401 Authorization header missing or malformed. Send `Authorization: Bearer ds_live_…` on every request.
invalid-key 401 API key is malformed (wrong prefix) or unknown. Confirm the key in your secret manager matches the dashboard.
revoked-key 401 API key was revoked (manually or via rotation grace expiry). Mint a new key from the dashboard.
expired-key 401 API key passed its `expires_at` (set by rotation). Roll to the post-rotation key; old key auto-expires after the 24h grace window.
invalid-auth-token 401 Signup-verification / password-reset / magic-link token was malformed, consumed, or expired. Request a fresh token via the dashboard.
email-not-verified 401 Login attempted before the user verified their signup email. Click the verify link in the welcome email, or use Resend verification email on /verify-email.
invalid-credentials 401 Login failed (wrong password) OR account does not exist — both surface the same shape on purpose (no enumeration). Verify credentials; do not retry-loop.
forbidden 403 Key is valid but lacks the required scope for this endpoint. Mint a key with the right scope (see /docs/api-keys).
mfa-step-up-required 403 Endpoint requires a fresh MFA step-up confirmation. Extension `requires_mfa_step_up: true`. POST a 6-digit code to /v1/auth/mfa/step-up, then retry the original request.
legal-acceptance-required 403 A new ToS / Privacy revision hasn't been accepted by the calling account. POST to /v1/account/legal/accept with the current revision id.
not-found 404 Resource id does not exist OR you do not have access (both surface as 404; no enumeration). Verify the id; cross-account lookups return 404 by design.
conflict 409 State conflict (duplicate, sticky tombstone, idempotency-key replay with different body). Read `detail` — often "mint a fresh resource instead".
concurrency-limit 429 Account has hit its tier-bound concurrent-session cap. Extensions `current_sessions` + `limit`. Wait for a running session to finish, or upgrade the tier. See /docs/concurrency.
tier-limit 429 Tier cap reached on a non-session resource (e.g. profile count, API keys per account). Upgrade tier or delete unused resources.
rate-limited 429 A rate-limit bucket is empty. Extension `retry_after_seconds`; same value as `Retry-After` header. Honour Retry-After. See /docs/rate-limits.
session-destroyed 410 Tried to operate on a session that's already been destroyed. Treat the session as gone; create a new one.
session-timeout 504 A blocking session operation (e.g. wait-for-selector) ran past its timeout. Increase the timeout if legitimate, or address the upstream slowness.
internal 500 Driftstack hit an unexpected error processing the request. Retry with exponential backoff. If persistent, email support with X-Request-Id.
driver-error 502 The underlying browser driver returned a structured failure. See `detail` for the driver-side cause. Often retryable.
driver-not-integrated 503 Endpoint hit a driver path that hasn't been wired up yet. Not retryable; contact support if a documented path lands here.
feature-unavailable 503 Feature explicitly disabled at deploy time (e.g. R2 bucket unconfigured for avatar upload). Not retryable; the operator must wire the missing dependency.
byok-anthropic-required 502 Agent-sessions message turn cannot resolve an Anthropic API key. BYOK-for-v1.0 means the customer must supply their own key. PUT /v1/account/me/byok-anthropic-key to set a stored key, or supply x-byok-anthropic-api-key on each request header.
bundled-llm-budget-exhausted 402 Bundled-LLM monthly soft-cap reached on this account (default $20; configurable via PATCH /v1/account/me/bundled-llm-settings). Resets at calendar month start. Raise the cap, wait until next month, or supply a BYOK Anthropic key (BYOK always wins per Q4=A).
bundled-llm-consent-required 402 Deployment is wired for bundled-LLM but the customer has not yet ticked the consent flag. Distinct from byok-anthropic-required so dashboards can render a one-click "enable bundled-LLM" CTA. PATCH /v1/account/me/bundled-llm-settings with bundled_llm_consent: true.
pair-mode-conflict 409 Pair-mode takeover lost the SET-NX-EX race against another participant. Extension carries winner_client_id so the loser dashboard can render "user X is taking over". Re-fetch the session; do not retry the takeover unless the winner releases the lock.
pair-mode-invalid-transition 409 Pair-mode state machine refused the requested transition (e.g. a handback request before any takeover-grant). Extensions `from` + `transition`. Re-fetch the pair-mode state; do not retry the transition without resolving the state mismatch.
email-already-registered 409 Signup attempted for an email that already has an account. Direct the user to /login or to the password-reset flow.

Stability

The type URI slugs listed above are part of the API contract — they will not be renamed or removed without a deprecation window. New types may be added; defensive clients should treat unknown types as internal (i.e. retry with backoff and surface detail to the operator).

OAuth token endpoint

Errors from the OAuth token endpoint follow the standard problem shape (the server maps OAuth 2.0 errors via UnauthorizedError / BadRequestError):

Status outages

For platform-level outages (everyone hits 5xx), check status.driftstack.dev. Subscribe to email notifications from the same page if you'd like to be told before your dashboard refreshes.

Support

Persistent or surprising errors: [email protected]. Include the X-Request-Id header from the response — every Driftstack response carries one, and we use it to find the corresponding server logs.