Skip to main content

Error Handling

Comprehensive guide to handling API errors.

Error Response Format

All errors return consistent JSON format:

{
"detail": "Error description explaining what went wrong"
}

HTTP Status Codes

2xx Success

200 - OK

Request succeeded.

{
"eligible": true,
"coverage_pct": 80.0
}

4xx Client Errors

400 - Bad Request

Invalid request format or missing required fields.

{
"detail": "Invalid request format: missing field 'patient_data'"
}

Common causes:

  • Missing required fields
  • Invalid field types
  • Malformed JSON
  • Invalid data format

Recovery:

  1. Check request structure against endpoint documentation
  2. Verify all required fields are present
  3. Validate data types (strings, numbers, booleans)
  4. Retry request

401 - Unauthorized

Invalid or missing API key.

{
"detail": "Unauthorized: Invalid API key"
}

Common causes:

  • Missing Authorization header
  • Expired API key
  • Invalid API key

Recovery:

  1. Check API key is included in header: Authorization: Bearer YOUR_KEY
  2. Verify key hasn't expired
  3. Generate new key if needed

404 - Not Found

Requested resource doesn't exist.

{
"detail": "Procedure code PROC999 not found"
}

Common causes:

  • Invalid procedure code
  • Invalid drug code
  • Unsupported endpoint

Recovery:

  1. Check procedure/drug code against reference tables
  2. Verify endpoint URL is correct
  3. Contact support for new codes

422 - Validation Error

Request body validation failed.

{
"detail": "Validation error: data_hash must be 64 hex characters, got 'invalid_hash'"
}

Common causes:

  • Invalid data format (e.g., non-hex for hash)
  • Field length violation
  • Invalid IPFS CID format

Recovery:

  1. Check field format against specification
  2. Verify data constraints (length, character set)
  3. Validate encoding (Base64 for encrypted data, hex for hashes)

429 - Too Many Requests

Rate limit exceeded.

{
"detail": "Rate limit exceeded: 100 requests per minute. Try again in 45 seconds."
}

Headers:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1705330800

Common causes:

  • Sending too many requests per minute
  • Burst of requests

Recovery:

  1. Implement exponential backoff
  2. Cache results when possible
  3. Upgrade to higher rate limit tier
  4. Use batch endpoints for multiple requests

5xx Server Errors

500 - Internal Server Error

Server encountered unexpected condition.

{
"detail": "Eligibility check failed: ZK proof generation timed out"
}

Common causes:

  • Proof generation timeout
  • Blockchain connection error
  • Database error
  • Encryption/decryption failure

Recovery:

  1. Retry request after 30 seconds
  2. Check server status at /status endpoint
  3. Contact support if error persists

503 - Service Unavailable

Server is temporarily unavailable.

{
"detail": "Service temporarily unavailable. Please try again later."
}

Common causes:

  • API server down
  • Database maintenance
  • Blockchain node offline
  • Deployment in progress

Recovery:

  1. Wait 30-60 seconds and retry
  2. Check status page
  3. Contact support

Error Handling Patterns

Retry Logic

Implement exponential backoff for transient errors:

from tenacity import retry, stop_after_attempt, wait_exponential

@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=4, max=10)
)
def call_api_with_retry(endpoint, payload):
response = requests.post(endpoint, json=payload)
response.raise_for_status() # Raise exception for non-2xx codes
return response.json()

try:
result = call_api_with_retry(
"http://localhost:8000/verify-eligibility",
payload
)
except Exception as e:
print(f"Failed after retries: {e}")

Error Classification

Classify errors by type for different handling:

def classify_error(response):
if response.status_code == 429:
return "rate_limit"
elif response.status_code in [500, 503]:
return "transient"
elif response.status_code in [400, 422]:
return "validation"
else:
return "unknown"

def handle_error(response):
error_type = classify_error(response)

if error_type == "rate_limit":
wait_time = int(response.headers.get('X-RateLimit-Reset', 60))
print(f"Rate limited. Wait {wait_time} seconds")

elif error_type == "transient":
print("Server error. Retrying...")

elif error_type == "validation":
print(f"Invalid request: {response.json()['detail']}")

else:
print("Unknown error")

Error Logging

Log errors for debugging and monitoring:

import logging
import json

logger = logging.getLogger(__name__)

def call_api_with_logging(endpoint, payload):
try:
response = requests.post(endpoint, json=payload)
response.raise_for_status()
return response.json()

except requests.exceptions.HTTPError as e:
error_detail = e.response.json().get('detail', 'Unknown error')
logger.error(
f"API error",
extra={
"endpoint": endpoint,
"status_code": e.response.status_code,
"error": error_detail,
"payload": json.dumps(payload)
}
)
raise

Common Error Scenarios

Scenario 1: Decryption Failure

Error:

{
"detail": "Eligibility check failed: Decryption failed"
}

Cause: Encrypted data doesn't match decryption key

Solution:

# Ensure consistent encryption key
encryption_key = load_key_from_secure_storage()

# Verify data was encrypted with same key
cipher = Fernet(encryption_key)
try:
decrypted = cipher.decrypt(encrypted_data)
except Exception as e:
print(f"Decryption failed: {e}")
# Regenerate encrypted data with correct key

Scenario 2: Hash Mismatch

Error:

{
"detail": "Eligibility check failed: Data hash mismatch"
}

Cause: data_hash doesn't match hash of encrypted_data

Solution:

import hashlib

# Recompute hash correctly
patient_data = {...}
correct_hash = hashlib.sha256(
json.dumps(patient_data).encode()
).hexdigest()

payload = {
"patient_data": {
...
"data_hash": correct_hash # Use computed hash
}
}

Scenario 3: Invalid Procedure Code

Error:

{
"detail": "Procedure code PROC999 not found"
}

Solution:

# Check procedure code against reference
VALID_PROCEDURES = ['PROC001', 'PROC002', 'PROC003', ...]

if procedure_code not in VALID_PROCEDURES:
print(f"Invalid procedure: {procedure_code}")
print(f"Valid options: {VALID_PROCEDURES}")
# Use correct procedure code

Scenario 4: Rate Limit

Error:

{
"detail": "Rate limit exceeded: 100 requests per minute"
}

Solution:

import time

def api_call_with_rate_limit_handling(endpoint, payload):
response = requests.post(endpoint, json=payload)

if response.status_code == 429:
# Extract wait time from header
reset_time = int(response.headers.get('X-RateLimit-Reset'))
wait_seconds = reset_time - time.time()

print(f"Rate limited. Waiting {wait_seconds} seconds...")
time.sleep(wait_seconds)

# Retry request
return api_call_with_rate_limit_handling(endpoint, payload)

return response.json()

Scenario 5: Timeout

Error:

{
"detail": "Request timeout after 30 seconds"
}

Solution:

import requests

# Use timeout parameter
response = requests.post(
"http://localhost:8000/verify-eligibility",
json=payload,
timeout=30 # 30 seconds
)

# Implement circuit breaker pattern
from circuitbreaker import circuit

@circuit(failure_threshold=5, recovery_timeout=60)
def call_api_with_circuit_breaker(endpoint, payload):
return requests.post(endpoint, json=payload)

Monitoring and Alerting

Error Metrics to Track

metrics = {
"total_requests": 1000,
"successful": 950,
"failed": 50,
"error_rate": "5.0%",

"by_type": {
"validation": 20,
"transient": 15,
"rate_limit": 10,
"authorization": 5
},

"latency": {
"p50": 400, # milliseconds
"p95": 800,
"p99": 1200
}
}

Alert Thresholds

Set up alerts for:

  • Error rate > 5%
  • Response time > 5 seconds (p95)
  • Sustained 5xx errors
  • Rate limit hits

Best Practices

  1. Always handle errors gracefully - Never crash on API error
  2. Log all errors - For debugging and monitoring
  3. Implement retry logic - With exponential backoff
  4. Cache results - To reduce API calls
  5. Monitor error rates - Set up alerts
  6. Use circuit breakers - Prevent cascading failures
  7. Validate input - Before sending to API
  8. Document expected errors - For client developers

Next Steps