Error Handling¶
Learn how to handle errors gracefully when working with the Sherpai Partner API.
Overview¶
The Sherpai Partner API returns standard HTTP status codes to indicate the success or failure of requests. This guide covers common error scenarios and how to handle them in your applications.
HTTP Status Codes¶
Success Codes¶
| Code | Description | When Used |
|---|---|---|
200 |
OK | Successful GET, PUT, PATCH requests |
201 |
Created | Successful POST requests that create resources |
204 |
No Content | Successful DELETE requests |
Client Error Codes¶
| Code | Description | Common Causes |
|---|---|---|
400 |
Bad Request | Invalid request format or parameters |
401 |
Unauthorized | Missing or invalid authentication token |
403 |
Forbidden | Valid token but insufficient permissions |
404 |
Not Found | Resource does not exist |
422 |
Unprocessable Entity | Request validation failed |
429 |
Too Many Requests | Rate limit exceeded |
Server Error Codes¶
| Code | Description | Action |
|---|---|---|
500 |
Internal Server Error | Retry request after delay |
502 |
Bad Gateway | Temporary issue, retry with backoff |
503 |
Service Unavailable | Service temporarily down |
Error Response Format¶
All errors return a consistent JSON format with detailed information:
Standard Error Response¶
{
"detail": "Human-readable error message",
"error_code": "ERROR_CODE",
"status_code": 400,
"status": "error",
"timestamp": "2024-01-01T12:00:00Z",
"request_id": "abc123-def456-ghi789"
}
Validation Error Response (422)¶
For validation errors, additional field-level details are provided:
{
"detail": [
{
"loc": ["body", "email"],
"msg": "field required",
"type": "value_error.missing",
"ctx": {}
},
{
"loc": ["query", "brand_id"],
"msg": "ensure this value is greater than or equal to 1",
"type": "value_error.number.not_ge",
"ctx": {"limit_value": 1}
}
],
"error_code": "VALIDATION_ERROR",
"status_code": 422,
"status": "error",
"timestamp": "2024-01-01T12:00:00Z",
"request_id": "abc123-def456-ghi789"
}
Error Response Fields¶
| Field | Type | Description |
|---|---|---|
detail |
string or array | Human-readable error message(s) |
error_code |
string | Machine-readable error code (see Error Codes) |
status_code |
integer | HTTP status code |
status |
string | Always "error" for error responses |
timestamp |
string | ISO 8601 timestamp of the error |
request_id |
string | Unique request identifier for support |
retry_after |
integer | Seconds to wait before retrying (for 429 errors) |
Error Codes¶
The API uses standardized error codes for programmatic error handling:
Authentication Errors¶
INVALID_CREDENTIALS- Missing or invalid credentialsTOKEN_INVALID- Token format is invalidTOKEN_EXPIRED- Token has expiredUSER_DISABLED- User account is disabledTOO_MANY_ATTEMPTS- Too many failed login attempts
Authorization Errors¶
INSUFFICIENT_PERMISSIONS- User lacks required permissionsROLE_REQUIRED- Specific role requiredCOMPANY_ACCESS_DENIED- Access denied to company/brand
Validation Errors¶
VALIDATION_ERROR- General validation errorINVALID_DATE_FORMAT- Invalid date formatINVALID_PLATFORM- Invalid platform identifierINVALID_METRICS- Invalid metrics parameterINVALID_ORDER_BY- Invalid order_by parameterINVALID_CURSOR- Invalid pagination cursorREQUIRED_FIELD_MISSING- Required field is missingVALUE_OUT_OF_RANGE- Value outside allowed range
Resource Errors¶
BRAND_NOT_FOUND- Brand does not existPOST_NOT_FOUND- Post does not existUSER_NOT_FOUND- User does not existACCOUNT_NOT_FOUND- Account does not exist
Rate Limiting¶
RATE_LIMIT_EXCEEDED- Rate limit exceededQUOTA_EXCEEDED- Quota exceeded
Server Errors¶
INTERNAL_SERVER_ERROR- Internal server errorSERVICE_UNAVAILABLE- Service temporarily unavailableDATABASE_ERROR- Database operation failedFIREBASE_ERROR- Firebase service errorSOCIAL_MEDIA_API_ERROR- External social media API error
Common Error Scenarios¶
Authentication Errors¶
Missing Token¶
Response:
{
"detail":"Authentication required. Please provide a valid Bearer token in the Authorization header.","error_code":"INVALID_CREDENTIALS","status_code":401,"status":"error","timestamp":"2025-11-12T14:09:16.804787Z","request_id":"d90abe92-da54-4e51-afe5-5e256ba94858"
}
Invalid Token¶
Response:
{
"detail":"Authentication error: Invalid authentication token format. Please check your credentials.. Please try again or contact support if the issue persists.","error_code":"TOKEN_INVALID","status_code":401,"status":"error","timestamp":"2025-11-12T14:08:14.373052Z","request_id":"af9dd27e-2b22-4f82-b354-098d275e89b9"
}
Expired Token¶
{
"detail":"Your session has expired. Please log in again.","error_code":"TOKEN_EXPIRED","status_code":401,"status":"error","timestamp":"2025-11-12T14:09:39.617970Z","request_id":"bbc01148-4ded-4bc2-8b66-ff9d21977c59"
}
Solution: Re-authenticate to get a new token.
Validation Errors¶
Missing Required Fields¶
curl -X POST https://partner-api.sherp.ai/auth/login \
-H "Content-Type: application/json" \
-d '{}'
Response:
{
"detail": [
{
"loc": ["body", "email"],
"msg": "field required",
"type": "value_error.missing"
},
{
"loc": ["body", "password"],
"msg": "field required",
"type": "value_error.missing"
}
]
}
Invalid Parameter Values¶
Response:
{
"detail": [
{
"loc": ["query", "brand_id"],
"msg": "ensure this value is greater than or equal to 1",
"type": "value_error.number.not_ge"
}
]
}
Rate Limiting (429)¶
When you exceed the rate limit, you'll receive a detailed error response:
Response:
{
"detail": "Rate limit exceeded. Please wait 2 minutes and 30 seconds before making another request.",
"error_code": "RATE_LIMIT_EXCEEDED",
"status_code": 429,
"status": "error",
"timestamp": "2024-01-01T12:00:00Z",
"request_id": "abc123-def456-ghi789",
"retry_after": 150
}
Response Headers:
X-RateLimit-Limit: 10000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1704067200
X-Usage-Total: 10000
X-Usage-Period: hourly
Retry-After: 150
Solution: Wait for the time specified in retry_after (seconds) before retrying. Implement exponential backoff for retries.
See Rate Limiting Guide for detailed information.
Retryability Guidance¶
When encountering errors, use the following guidance to determine whether and how to retry:
| Status Code | Retry Immediately? | Retry with Backoff? | Never Retry? | Notes |
|---|---|---|---|---|
| 200-299 | N/A | N/A | N/A | Success - no retry needed |
| 400 | ❌ | ❌ | ✅ | Bad request - fix and retry manually |
| 401 | ❌ | ❌ | ✅ | Auth error - re-authenticate first |
| 403 | ❌ | ❌ | ✅ | Permission error - check roles |
| 404 | ❌ | ❌ | ✅ | Not found - verify resource exists |
| 422 | ❌ | ❌ | ✅ | Validation error - fix request |
| 429 | ❌ | ✅ | ❌ | Rate limited - use exponential backoff |
| 500 | ✅* | ✅ | ❌ | Server error - retry with backoff |
| 502 | ❌ | ✅ | ❌ | Bad gateway - retry with backoff |
| 503 | ❌ | ✅ | ❌ | Service unavailable - retry with backoff |
*Only retry immediately for idempotent operations (GET requests)
Error Code → Action Mapping¶
| Error Code | HTTP Status | Retry Strategy | Action |
|---|---|---|---|
TOKEN_EXPIRED |
401 | Re-authenticate and retry once | Get new token via /auth/login |
TOKEN_INVALID |
401 | Do not retry | Log error, surface to operator |
INVALID_CREDENTIALS |
401 | Do not retry | Escalate to admin / support |
RATE_LIMIT_EXCEEDED |
429 | Retry with exponential backoff | Wait for retry_after seconds |
INTERNAL_SERVER_ERROR |
500 | Retry with backoff (idempotent only) | Retry after delay |
SERVICE_UNAVAILABLE |
503 | Retry with exponential backoff | Wait and retry |
Best Practices¶
1. Implement Retry Logic¶
For temporary errors (5xx), implement exponential backoff:
import time
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
def create_session_with_retries():
session = requests.Session()
retry_strategy = Retry(
total=3,
backoff_factor=1,
status_forcelist=[429, 500, 502, 503, 504],
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("http://", adapter)
session.mount("https://", adapter)
return session
2. Handle Authentication Errors¶
Automatically refresh tokens when they expire:
def make_authenticated_request(url, token):
headers = {'Authorization': f'Bearer {token}'}
response = requests.get(url, headers=headers)
if response.status_code == 401:
# Token expired, re-authenticate
new_token = login()
headers = {'Authorization': f'Bearer {new_token}'}
response = requests.get(url, headers=headers)
return response
3. Validate Input Before Sending¶
Check required fields and formats client-side:
function validateLoginData(email, password) {
if (!email || !password) {
throw new Error('Email and password are required');
}
if (!email.includes('@')) {
throw new Error('Invalid email format');
}
return { email, password };
}
4. Log Errors for Debugging¶
import logging
logger = logging.getLogger(__name__)
def handle_api_error(response):
if response.status_code >= 400:
logger.error(f"API Error {response.status_code}: {response.text}")
if response.status_code == 401:
logger.info("Authentication required")
elif response.status_code == 429:
logger.warning("Rate limit exceeded")
elif response.status_code >= 500:
logger.error("Server error, retrying...")
return response
Error Handling Examples¶
JavaScript/Node.js¶
async function safeApiCall(url, options = {}) {
try {
const response = await fetch(url, options);
if (!response.ok) {
const error = await response.json();
throw new Error(`API Error ${response.status}: ${error.detail}`);
}
return await response.json();
} catch (error) {
console.error('API call failed:', error.message);
// Handle specific error types
if (error.message.includes('401')) {
// Handle authentication error
await refreshToken();
return safeApiCall(url, options); // Retry
}
throw error;
}
}
Python¶
import requests
from typing import Dict, Any
class APIError(Exception):
def __init__(self, status_code: int, detail: str):
self.status_code = status_code
self.detail = detail
super().__init__(f"API Error {status_code}: {detail}")
def safe_api_call(url: str, **kwargs) -> Dict[Any, Any]:
response = requests.get(url, **kwargs)
if response.status_code >= 400:
try:
error_data = response.json()
detail = error_data.get('detail', 'Unknown error')
except:
detail = response.text or 'Unknown error'
raise APIError(response.status_code, detail)
return response.json()
# Usage
try:
data = safe_api_call('https://partner-api.sherp.ai/me',
headers={'Authorization': f'Bearer {token}'})
except APIError as e:
if e.status_code == 401:
print("Need to re-authenticate")
elif e.status_code == 429:
print("Rate limited, waiting...")
time.sleep(60)
else:
print(f"API error: {e.detail}")
Testing Error Scenarios¶
Unit Tests¶
import pytest
from unittest.mock import Mock
def test_handle_401_error():
mock_response = Mock()
mock_response.status_code = 401
mock_response.json.return_value = {"detail": "Could not validate credentials"}
with pytest.raises(APIError) as exc_info:
handle_response(mock_response)
assert exc_info.value.status_code == 401
assert "credentials" in exc_info.value.detail
Next Steps¶
- Rate Limiting - Understand API usage limits
- Authentication - Token management best practices
- FAQ - Common questions and solutions
- Support - Get help from our team