Error Reference
A single reference for every error you can encounter using Ravi — CLI auth errors, REST API HTTP status codes, and error response shapes.
CLI auth errors
These codes appear in ravi auth status --json and in CLI stderr output.
Auth status response shapes
ravi auth status --json returns one of three shapes:
// Authenticated
{ "authenticated": true, "email": "you@example.com", "has_encryption": true }
// Token expired
{ "authenticated": false, "error": "token_expired", "message": "Access token expired and refresh failed." }
// Not logged in
{ "authenticated": false, "error": "not_authenticated", "message": "No credentials found. Run ravi auth login." }
Use authenticated as the boolean gate in automation scripts — do not parse the message string.
Auth error codes
| Error | Cause | Recovery |
|---|---|---|
device_code_expired | Device code was not authorized within 15 minutes | Re-run ravi auth login and complete the OAuth flow promptly |
pin_locked | PIN entered incorrectly 3 times | Run ravi auth recover and enter your recovery key from ~/.ravi/recovery-key.txt |
token_expired | Refresh token invalidated (e.g. password change, logout from another device) | Re-run ravi auth login; in headless environments, rotate RAVI_ACCESS_TOKEN |
encryption_missing | auth.json exists but PIN was never set | Run ravi auth setup-encryption to initialize the vault |
identity_not_found | Config references a deleted Identity UUID | Run ravi identity list and update .ravi/config.json with a valid UUID |
not_authenticated | No credentials found at all | Run ravi auth login; in CI/containers, set RAVI_ACCESS_TOKEN env var |
Guarding long-running processes
check_auth() {
local status
status=$(ravi auth status --json 2>/dev/null)
if ! echo "$status" | jq -e '.authenticated' > /dev/null 2>&1; then
echo "Ravi auth failed — re-login required" >&2
exit 1
fi
}
import subprocess, json, sys
def check_auth():
result = subprocess.run(
["ravi", "auth", "status", "--json"],
capture_output=True, text=True
)
try:
status = json.loads(result.stdout)
except json.JSONDecodeError:
sys.exit("ravi auth status returned unexpected output")
if not status.get("authenticated"):
sys.exit(f"Ravi auth error: {status.get('error')} — re-login required")
REST API errors
HTTP status codes
All REST API endpoints return standard HTTP status codes. Error responses include a JSON body.
| Status | Meaning | Common cause | Recovery |
|---|---|---|---|
| 400 | Bad request | Missing required field, invalid format | Check the request body against the endpoint schema |
| 401 | Unauthorized | Missing or expired Bearer token | Refresh via POST /api/auth/token/refresh/ or re-authenticate |
| 402 | Payment required | Action requires an active Ravi subscription | Upgrade at ravi.app/billing |
| 403 | Forbidden | Token valid but missing scope, or identity not owned by authenticated account | Verify X-Ravi-Identity UUID belongs to the authenticated account |
| 404 | Not found | UUID does not exist or was deleted | Re-list the resource to confirm it still exists |
| 409 | Conflict | Identity name or email already taken | Choose a different name or list existing identities |
| 429 | Rate limited | Too many requests | Back off and retry; email send limits are 60/hr and 500/day |
| 500 | Server error | Unexpected server-side failure | Retry with exponential backoff; report persistent errors |
Error response shape
Standard error:
{
"detail": "Authentication credentials were not provided.",
"code": "not_authenticated"
}
Validation error (400) — field-level detail:
{
"name": ["This field is required."]
}
Token refresh
When a 401 is returned with "code": "token_expired", refresh the access token:
curl -s -X POST https://ravi.app/api/auth/token/refresh/ \
-H "Content-Type: application/json" \
-d '{"refresh": "<refresh_token>"}' \
| jq -r .access
import httpx
def refresh_token(refresh_token: str) -> str:
resp = httpx.post(
"https://ravi.app/api/auth/token/refresh/",
json={"refresh": refresh_token},
)
resp.raise_for_status()
return resp.json()["access"]
The refresh token is stored in ~/.ravi/auth.json under the refresh_token key, or passed via environment for headless deployments.
Rate limits
| Operation | Limit |
|---|---|
| Email send | 60 / hour, 500 / day |
| Identity create | Contact support for bulk limits |
| Inbox polling | No documented hard limit — use 2–10 s intervals |
See also
- Authentication — interactive and headless auth setup, token injection for CI/containers
- Troubleshooting & FAQ — common runtime errors with step-by-step fixes
- API Endpoints — full REST API endpoint reference