Error Codes Reference
ZhenRent uses conventional HTTP status codes and standardized error responses.
Error Response Format
All errors follow this structure:
{
"error": {
"code": "error_code_here",
"message": "Human-readable error description",
"details": {} // Optional: Additional context
}
}
HTTP Status Codes
| Status Code | Meaning | When It Occurs |
|---|---|---|
| 400 | Bad Request | Invalid request parameters |
| 401 | Unauthorized | Missing or invalid API key |
| 402 | Payment Required | Insufficient balance |
| 403 | Forbidden | Insufficient permissions |
| 404 | Not Found | Resource doesn't exist |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Platform error |
| 503 | Service Unavailable | Service temporarily down |
Error Code Categories
Authentication Errors (401, 403)
invalid_api_key
Status: 401 Unauthorized
Cause: API key is invalid, expired, or revoked
Response:
{
"error": {
"code": "invalid_api_key",
"message": "Invalid or expired API key"
}
}
Solution:
- Verify API key format starts with
zr_live_orzr_test_ - Check Authorization header:
Bearer YOUR_KEY - Confirm key hasn't been deleted in dashboard
- Create new API key if necessary
authentication_required
Status: 401 Unauthorized
Cause: Missing Authorization header
Response:
{
"error": {
"code": "authentication_required",
"message": "Authentication required. Please provide API key in Authorization header."
}
}
Solution:
headers = {
"Authorization": f"Bearer {API_KEY}"
}
insufficient_permissions
Status: 403 Forbidden
Cause: API key lacks required permissions
Response:
{
"error": {
"code": "insufficient_permissions",
"message": "API key does not have permission to perform this action",
"required_permission": "task:create"
}
}
Solution: Add required permission to API key in dashboard
Payment Errors (402)
insufficient_balance
Status: 402 Payment Required
Cause: Account balance too low to create task
Response:
{
"error": {
"code": "insufficient_balance",
"message": "Account balance (3000 cents) insufficient for task cost (5000 cents)",
"balance_cents": 3000,
"required_cents": 5000
}
}
Solution:
- Check current balance:
GET /api/v1/payment/balance - Recharge account via dashboard
- Retry task creation
Validation Errors (400)
validation_error
Status: 400 Bad Request
Cause: One or more request parameters are invalid
Response:
{
"error": {
"code": "validation_error",
"message": "Request validation failed",
"details": [
{
"type": "greater_than_equal",
"loc": ["body", "budget_cents"],
"msg": "Input should be greater than or equal to 1000",
"input": 500
},
{
"type": "future_date",
"loc": ["body", "deadline_at"],
"msg": "Deadline must be in the future",
"input": "2023-01-01T00:00:00Z"
}
]
}
}
Common validation issues:
| Field | Issue | Solution |
|---|---|---|
budget_cents | < 1000 | Use at least 1000 (10 RMB) |
deadline_at | Past date | Use future ISO 8601 date |
latitude | Out of range | Use -90 to 90 |
longitude | Out of range | Use -180 to 180 |
title | Too long | Max 200 characters |
description | Missing | Required field |
Solution: Fix parameter values based on details array
invalid_json
Status: 400 Bad Request
Cause: Malformed JSON in request body
Response:
{
"error": {
"code": "invalid_json",
"message": "Invalid JSON in request body",
"details": "Expecting ',' delimiter: line 5 column 3"
}
}
Solution: Validate JSON syntax before sending
Resource Errors (404)
task_not_found
Status: 404 Not Found
Cause: Task doesn't exist or not owned by your account
Response:
{
"error": {
"code": "task_not_found",
"message": "Task not found or you don't have access"
}
}
Solution:
- Verify task ID is correct
- Confirm you own the task
- Check task wasn't deleted
result_not_available
Status: 404 Not Found
Cause: Attempted to retrieve result for incomplete task
Response:
{
"error": {
"code": "result_not_available",
"message": "Task is still in progress",
"current_status": "in_progress"
}
}
Solution: Wait until task status is completed
Rate Limit Errors (429)
rate_limit_exceeded
Status: 429 Too Many Requests
Cause: Exceeded API request rate limit
Response:
{
"error": {
"code": "rate_limit_exceeded",
"message": "Too many requests. Please retry after 60 seconds.",
"retry_after": 60
}
}
Headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1680261600
Retry-After: 60
Solution: Implement exponential backoff
import time
def api_request_with_backoff(func, max_retries=3):
for attempt in range(max_retries):
try:
return func()
except RateLimitError as e:
if attempt == max_retries - 1:
raise
wait_time = 2 ** attempt # 1s, 2s, 4s
time.sleep(wait_time)
Server Errors (500, 503)
internal_error
Status: 500 Internal Server Error
Cause: Unexpected server-side error
Response:
{
"error": {
"code": "internal_error",
"message": "An internal error occurred. Please try again.",
"request_id": "req_abc123"
}
}
Solution:
- Retry request (may be transient)
- If persists, contact support with
request_id
service_unavailable
Status: 503 Service Unavailable
Cause: Service temporarily unavailable (maintenance or overload)
Response:
{
"error": {
"code": "service_unavailable",
"message": "Service temporarily unavailable. Please try again later.",
"retry_after": 300
}
}
Solution: Retry after specified seconds
Error Handling Best Practices
1. Always Check Status Code
response = requests.post(url, json=data, headers=headers)
if response.status_code == 201:
task = response.json()
print(f"Success: {task['task_id']}")
elif response.status_code == 402:
print("Insufficient balance. Please recharge.")
elif response.status_code >= 500:
print("Server error. Please retry.")
else:
print(f"Error: {response.json()}")
2. Implement Retry Logic
import time
import requests
from requests.exceptions import RequestException
def api_call_with_retry(func, max_retries=3):
"""Retry API calls with exponential backoff"""
for attempt in range(max_retries):
try:
response = func()
response.raise_for_status()
return response.json()
except requests.HTTPError as e:
if e.response.status_code in [500, 502, 503, 504]:
# Retry server errors
if attempt < max_retries - 1:
wait_time = 2 ** attempt
time.sleep(wait_time)
continue
elif e.response.status_code == 429:
# Respect rate limit
retry_after = int(e.response.headers.get('Retry-After', 60))
time.sleep(retry_after)
continue
raise
except RequestException as e:
# Retry network errors
if attempt < max_retries - 1:
time.sleep(2 ** attempt)
continue
raise
raise Exception("Max retries exceeded")
# Usage
def make_request():
return requests.post(url, json=data, headers=headers)
try:
result = api_call_with_retry(make_request)
print(f"Success: {result}")
except Exception as e:
print(f"Failed after retries: {e}")
3. Parse Error Details
def handle_api_error(response):
"""Extract meaningful error information"""
error_data = response.json()['error']
code = error_data['code']
message = error_data['message']
if code == 'validation_error':
# Extract field-specific errors
for detail in error_data.get('details', []):
field = detail['loc'][-1]
msg = detail['msg']
print(f"Validation error on '{field}': {msg}")
elif code == 'insufficient_balance':
balance = error_data['balance_cents'] / 100
required = error_data['required_cents'] / 100
print(f"Insufficient balance: {balance} RMB (need {required} RMB)")
else:
print(f"Error [{code}]: {message}")
# Usage
response = requests.post(url, json=data, headers=headers)
if not response.ok:
handle_api_error(response)
4. Log Request IDs
import logging
def make_api_request(url, **kwargs):
response = requests.post(url, **kwargs)
request_id = response.headers.get('X-Request-ID')
if response.ok:
logging.info(f"Request succeeded: {request_id}")
else:
logging.error(
f"Request failed: {request_id}",
extra={
'status_code': response.status_code,
'error': response.json()
}
)
return response
5. Implement Circuit Breaker
from datetime import datetime, timedelta
class CircuitBreaker:
def __init__(self, failure_threshold=5, timeout=60):
self.failure_threshold = failure_threshold
self.timeout = timeout
self.failures = 0
self.last_failure_time = None
self.state = 'closed' # closed, open, half-open
def call(self, func):
if self.state == 'open':
if datetime.now() - self.last_failure_time > timedelta(seconds=self.timeout):
self.state = 'half-open'
else:
raise Exception("Circuit breaker is open")
try:
result = func()
self.on_success()
return result
except Exception as e:
self.on_failure()
raise
def on_success(self):
self.failures = 0
self.state = 'closed'
def on_failure(self):
self.failures += 1
self.last_failure_time = datetime.now()
if self.failures >= self.failure_threshold:
self.state = 'open'
# Usage
breaker = CircuitBreaker()
try:
result = breaker.call(lambda: api_request())
except Exception as e:
print(f"Request failed: {e}")
Testing Error Scenarios
Test Validation Errors
# Test invalid budget
response = requests.post(url, json={
"budget_cents": 500, # Too low
# ... other fields
})
assert response.status_code == 400
assert response.json()['error']['code'] == 'validation_error'
Test Authentication Errors
# Test missing API key
response = requests.post(url, json=data) # No auth header
assert response.status_code == 401
# Test invalid API key
response = requests.post(url, json=data, headers={
"Authorization": "Bearer invalid_key"
})
assert response.status_code == 401
Test Rate Limiting
# Rapid requests to trigger rate limit
for i in range(150): # Exceed 100/min limit
response = requests.get(url, headers=headers)
if response.status_code == 429:
print(f"Rate limited after {i} requests")
break
Contact Support
If you encounter errors not documented here:
- Email: support@zhenrent.com
- Include:
- Error code and message
- Request ID (from
X-Request-IDheader) - Timestamp
- Request details (sanitize sensitive data)
Response time: < 24 hours