Error Handling
This guide covers the error response format and common error codes you may encounter when using the Veriglob API.
Error Response Format
Section titled “Error Response Format”All error responses follow this structure:
{ "status": "error", "message": "Error category", "error": "Detailed error message"}| Field | Description |
|---|---|
status | Always "error" for error responses |
message | A brief category or summary of the error |
error | Detailed, human-readable error description |
HTTP Status Codes
Section titled “HTTP Status Codes”Client Errors (4xx)
Section titled “Client Errors (4xx)”| Code | Name | Description |
|---|---|---|
| 400 | Bad Request | Invalid request body, missing required fields, or malformed data |
| 401 | Unauthorized | Missing or invalid API key/authentication token |
| 403 | Forbidden | Valid authentication but insufficient permissions |
| 404 | Not Found | Requested resource doesn’t exist |
| 409 | Conflict | Resource already exists (e.g., duplicate email on registration) |
| 429 | Too Many Requests | Rate limit exceeded |
Server Errors (5xx)
Section titled “Server Errors (5xx)”| Code | Name | Description |
|---|---|---|
| 500 | Internal Server Error | Unexpected server error |
| 502 | Bad Gateway | Upstream service unavailable |
| 503 | Service Unavailable | Service temporarily unavailable |
Common Error Scenarios
Section titled “Common Error Scenarios”Authentication Errors
Section titled “Authentication Errors”Missing API Key (401)
{ "status": "error", "message": "Unauthorized", "error": "API key is required"}Invalid API Key (401)
{ "status": "error", "message": "Unauthorized", "error": "Invalid API key"}Expired Token (401)
{ "status": "error", "message": "Unauthorized", "error": "Token has expired"}Validation Errors
Section titled “Validation Errors”Missing Required Field (400)
{ "status": "error", "message": "Validation Error", "error": "issuer_did is required"}Invalid DID Format (400)
{ "status": "error", "message": "Bad Request", "error": "Invalid DID format: must start with 'did:key:'"}Invalid Credential (400)
{ "status": "error", "message": "Bad Request", "error": "Invalid credential: signature verification failed"}Resource Errors
Section titled “Resource Errors”DID Not Found (404)
{ "status": "error", "message": "Not Found", "error": "DID not found"}Credential Not Found (404)
{ "status": "error", "message": "Not Found", "error": "Credential with ID 'urn:uuid:...' not found"}Rate Limiting
Section titled “Rate Limiting”Rate Limit Exceeded (429)
{ "status": "error", "message": "Rate limit exceeded", "error": "You have exceeded your rate limit of 10 requests per minute. Please wait 45 seconds before retrying."}Handling Errors in Code
Section titled “Handling Errors in Code”JavaScript/Node.js
Section titled “JavaScript/Node.js”async function createDID() { try { const response = await fetch('https://api.veriglob.com/v1/did', { method: 'POST', headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json', }, });
const data = await response.json();
if (!response.ok) { // Handle error based on status code switch (response.status) { case 401: throw new Error('Authentication failed: ' + data.error); case 429: // Implement retry with backoff const retryAfter = response.headers.get('X-RateLimit-Reset'); throw new Error(`Rate limited. Retry after ${retryAfter}`); default: throw new Error(data.error || 'Unknown error occurred'); } }
return data; } catch (error) { console.error('API Error:', error.message); throw error; }}Python
Section titled “Python”import requestsfrom time import sleep
def create_did(api_key): headers = { 'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json' }
response = requests.post( 'https://api.veriglob.com/v1/did', headers=headers )
if response.status_code == 429: # Handle rate limiting with retry retry_after = int(response.headers.get('X-RateLimit-Reset', 60)) sleep(retry_after) return create_did(api_key) # Retry
response.raise_for_status() return response.json()func createDID(apiKey string) (*DIDResponse, error) { req, _ := http.NewRequest("POST", "https://api.veriglob.com/v1/did", nil) req.Header.Set("Authorization", "Bearer "+apiKey)
client := &http.Client{} resp, err := client.Do(req) if err != nil { return nil, err } defer resp.Body.Close()
if resp.StatusCode == 429 { // Handle rate limiting retryAfter := resp.Header.Get("X-RateLimit-Reset") return nil, fmt.Errorf("rate limited, retry after: %s", retryAfter) }
if resp.StatusCode != 201 { var errResp ErrorResponse json.NewDecoder(resp.Body).Decode(&errResp) return nil, fmt.Errorf("API error: %s", errResp.Error) }
var result DIDResponse json.NewDecoder(resp.Body).Decode(&result) return &result, nil}Retry Strategy
Section titled “Retry Strategy”For transient errors (5xx, 429), implement exponential backoff:
async function fetchWithRetry(url, options, maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { try { const response = await fetch(url, options);
if (response.status === 429 || response.status >= 500) { const delay = Math.pow(2, i) * 1000; // Exponential backoff await new Promise((resolve) => setTimeout(resolve, delay)); continue; }
return response; } catch (error) { if (i === maxRetries - 1) throw error; } }}Best Practices
Section titled “Best Practices”- Always check the status code before processing the response body
- Log errors with context including request ID, endpoint, and timestamp
- Implement retry logic for transient errors with exponential backoff
- Handle rate limits gracefully by respecting the
X-RateLimit-Resetheader - Validate input on the client side to catch errors early
- Provide user-friendly messages by mapping error codes to actionable guidance