Error codes
Every Vorlek error returns the same envelope shape. Agents should branch on error.code, error.category, error.retry_safe, and error.fix.action; messages are human-readable context.
Envelope shape
{
"status": "error",
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded for provider 'sendgrid'. Retry after 12 seconds.",
"category": "user_input",
"retry_safe": true,
"provider": "sendgrid",
"fix": {
"action": "retry_after_delay",
"hint": "Respect the Retry-After header before retrying this provider."
}
},
"meta": {
"request_id": "01JD5K9X7M0Q8V0QY4FYVQ3WZV"
}
}
Category & retry semantics
| Category | Meaning | Typical response |
|---|---|---|
user_input | Caller input, account state, connection state, or selected tool/provider is wrong. | Fix input or state. Retry only when retry_safe is true. |
provider_fault | Provider-side terminal failure after Vorlek reached the provider. | Inspect provider reason, fix provider-side state, then retry. |
transient | Temporary Vorlek/provider/network condition. | Retry with exponential backoff. Honor Retry-After where present. |
system | Vorlek-side bug or unexpected internal state. | Report request_id; retry only if retry_safe. |
We surface two limits. X-RateLimit-* headers report short-window throttling (per-minute token bucket per provider). meta.quota reports your plan period quota. For most free-tier usage, meta.quota is what you hit first; the rate limiter fires on bursts.
Canonical code table
| HTTP | Code | Category | retry_safe | Recovery |
|---|---|---|---|---|
| 401 | AUTH_MISSING | user_input | false | Send Authorization: Bearer vk_live_.... |
| 401 | AUTH_INVALID | user_input | false | Verify or rotate the Vorlek API key. |
| 401 | AUTH_REVOKED | user_input | false | Rotate the API key and retry with the new value. |
| 403 | AUTH_FORBIDDEN | user_input | false | Re-issue/reconnect credentials with the required provider scope. |
| 409 | EMAIL_TAKEN | user_input | false | Sign in or use a different email. |
| 404 | ACCOUNT_NOT_FOUND | user_input | false | Contact support with request_id. |
| 409 | PROVIDER_ALREADY_CONNECTED | user_input | false | Disconnect or rotate the existing connection first. |
| 400 | PROVIDER_AUTH_INVALID | user_input | false | Reconnect the provider with a valid key and required scopes. |
| 404 | CONNECTION_NOT_FOUND | user_input | false | Run vorlek connect <provider> first. |
| 400 | CONNECTION_INVALID | user_input | false | Reconnect or rotate provider credentials. |
| 500 | CONNECTION_DECRYPT_FAILED | system | false | Reconnect provider; Vorlek should investigate by request_id. |
| 400 | INVALID_PARAMS | user_input | false | Fix the request body or CLI flags. |
| 400 | FIELD_TYPE_MISMATCH | user_input | false | Match the existing provider field type, or use a new property name. |
| 404 | NOT_FOUND | user_input | false | Check the resource identifier. |
| 413 | PAYLOAD_TOO_LARGE | user_input | false | Split or reduce the payload. |
| 501 | TOOL_NOT_SUPPORTED | user_input | false | Use a supported provider/tool pair. |
| 400 | TOOL_NOT_CONFIGURED | user_input | false | Reserved for Phase 5+ config-gated tools. |
| 429 | QUOTA_EXCEEDED | user_input | true | Wait for period reset or upgrade plan. |
| 429 | RATE_LIMITED | user_input | true | Honor the Retry-After header. |
| 409 | IDEMPOTENCY_CONFLICT | user_input | false | Use a fresh idempotency key for a different request body. |
| 429 | PROVIDER_RATE_LIMITED | transient | true | Honor provider retry hints or exponential backoff. |
| 503 | PROVIDER_UNAVAILABLE | transient | true | Retry after backoff. |
| 502 | PROVIDER_FAILED | provider_fault | false | Inspect provider-side reason, fix, then retry. |
| 500 | INTERNAL_ERROR | system | true | Server bug; report request_id. |
Recovery patterns for agents
if response.status == "success":
proceed()
elif response.error.retry_safe:
sleep_with_backoff()
retry()
elif response.error.code in ["AUTH_INVALID", "AUTH_REVOKED"]:
refresh_or_rotate_api_key()
retry_once()
elif response.error.code == "QUOTA_EXCEEDED":
show_upgrade_or_wait_until(response.meta.quota.resets_at)
else:
log(response.error, response.meta.request_id)
halt()
Lock note. This taxonomy is frozen-additive through v1.x. New codes may be added; renaming, removing, or changing semantics of existing codes is a major-version bump.