Error Handling
All errors return JSON with a consistent structure. This guide covers every error code you may encounter.
Error Response Formatโ
Every error follows this structure:
{
"error": true,
"code": "ERROR_CODE",
"message": "Human-readable description",
"details": {}
}
| Field | Type | Description |
|---|---|---|
error | boolean | Always true for errors |
code | string | Machine-readable error code |
message | string | Human-readable description |
details | object | Additional context (optional) |
HTTP Status Code Referenceโ
| Code | Name | Error Code | Description |
|---|---|---|---|
| 400 | Bad Request | INVALID_PARAMS | Invalid request parameters |
| 401 | Unauthorized | AUTH_REQUIRED | Authentication required or invalid |
| 402 | Payment Required | PAYMENT_REQUIRED | x402 payment needed |
| 403 | Forbidden | FORBIDDEN | Access denied to resource |
| 404 | Not Found | NOT_FOUND | Resource doesn't exist |
| 429 | Too Many Requests | RATE_LIMITED | Rate limit exceeded |
| 500 | Internal Server Error | SERVER_ERROR | Unexpected server error |
| 502 | Bad Gateway | BAD_GATEWAY | Upstream service error |
| 503 | Service Unavailable | SERVICE_UNAVAILABLE | Service temporarily down |
| 504 | Gateway Timeout | GATEWAY_TIMEOUT | Upstream timeout |
400 Bad Request โ INVALID_PARAMSโ
Invalid parameters or malformed request.
{
"error": true,
"code": "INVALID_PARAMS",
"message": "Invalid Ethereum address format",
"details": {
"field": "address",
"value": "not-an-address",
"expected": "0x followed by 40 hexadecimal characters"
}
}
Common Causesโ
| Error | Example | Fix |
|---|---|---|
| Invalid address | 0xinvalid | Use valid 0x + 40 hex chars |
| Invalid ENS name | test..eth | Use valid ENS format |
| Missing required param | {} | Include all required fields |
| Invalid JSON | {name:} | Fix JSON syntax |
| Invalid chain ID | chainId: 999999 | Use supported chain |
Handlingโ
try {
const result = await client.resolve('invalid..eth');
} catch (error) {
if (error.code === 'INVALID_PARAMS') {
console.error(`Invalid parameter: ${error.details?.field}`);
console.error(`Expected: ${error.details?.expected}`);
}
}
401 Unauthorized โ AUTH_REQUIREDโ
Authentication required or credentials are invalid.
{
"error": true,
"code": "AUTH_REQUIRED",
"message": "Invalid or expired authentication token",
"details": {
"reason": "token_expired",
"expiredAt": "2026-02-08T12:00:00Z"
}
}
Variationsโ
| Reason | Message | Solution |
|---|---|---|
missing_token | "Authentication required" | Add Authorization header |
invalid_token | "Invalid authentication token" | Check token format |
token_expired | "Token has expired" | Re-authenticate with SIWE |
invalid_api_key | "Invalid API key" | Check API key is correct |
revoked_key | "API key has been revoked" | Generate a new key |
Handlingโ
try {
const me = await client.getMe();
} catch (error) {
if (error.code === 'AUTH_REQUIRED') {
if (error.details?.reason === 'token_expired') {
// Re-authenticate
await client.signIn();
return client.getMe();
}
throw new Error('Please sign in first');
}
}
402 Payment Required โ PAYMENT_REQUIREDโ
Free tier exhausted; x402 micropayment required to continue.
{
"error": true,
"code": "PAYMENT_REQUIRED",
"message": "Free tier exhausted. Payment required to continue.",
"details": {
"price": "0.01",
"currency": "USDC",
"network": "base",
"chainId": 8453,
"receiver": "0xF499102c8707c6501CaAdD2028c6DF1c6C6E813b",
"paymentMethods": ["x402"],
"x402": {
"version": "1",
"facilitator": "https://api.cdp.coinbase.com/platform/v2/x402",
"maxAmountRequired": "10000",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"receiver": "0xF499102c8707c6501CaAdD2028c6DF1c6C6E813b"
}
}
}
x402 Payment Flowโ
- Receive 402 with payment details
- Sign EIP-712 payment authorization
- Include payment header in retry request
- Request succeeds with payment deducted
Handling with SDKโ
import { createX402Client } from '@x402/sdk';
const x402Client = createX402Client({
privateKey: process.env.WALLET_KEY,
network: 'base',
});
// SDK handles 402 automatically
const result = await x402Client.fetch(
'https://api.web3identity.com/api/ens/resolve/vitalik.eth'
);
Manual Handlingโ
async function fetchWithPayment(url) {
const response = await fetch(url);
if (response.status === 402) {
const error = await response.json();
const paymentHeader = await signX402Payment(error.details.x402);
return fetch(url, {
headers: {
'X-Payment': paymentHeader,
'Payment-Signature': paymentHeader
}
});
}
return response;
}
Relatedโ
403 Forbidden โ FORBIDDENโ
You don't have permission to access this resource.
{
"error": true,
"code": "FORBIDDEN",
"message": "Access denied to this resource",
"details": {
"reason": "insufficient_permissions",
"required": "admin",
"current": "user"
}
}
Common Causesโ
| Reason | Description |
|---|---|
insufficient_permissions | Your account lacks required permissions |
ip_blocked | Your IP has been blocked |
endpoint_restricted | Endpoint requires special access |
resource_private | Resource is not publicly accessible |
Handlingโ
try {
const data = await client.getAdminStats();
} catch (error) {
if (error.code === 'FORBIDDEN') {
console.error('You do not have permission to access this resource');
// Redirect to appropriate page or show error
}
}
404 Not Found โ NOT_FOUNDโ
The requested resource doesn't exist.
{
"error": true,
"code": "NOT_FOUND",
"message": "ENS name not registered",
"details": {
"resource": "ens_name",
"identifier": "nonexistent12345.eth",
"suggestion": "Check the name is registered at app.ens.domains"
}
}
Specific Error Codesโ
| Code | Resource | Description |
|---|---|---|
ENS_NOT_FOUND | ENS name | Name not registered |
ADDRESS_NOT_FOUND | Address | No data for this address |
TOKEN_NOT_FOUND | Token | Token not in our database |
CHAIN_NOT_FOUND | Chain | Unsupported chain ID |
ENDPOINT_NOT_FOUND | Endpoint | Invalid API endpoint |
Handlingโ
try {
const profile = await client.resolve('nonexistent12345.eth');
} catch (error) {
if (error.code === 'NOT_FOUND' || error.code === 'ENS_NOT_FOUND') {
return { exists: false, name: 'nonexistent12345.eth' };
}
throw error;
}
429 Too Many Requests โ RATE_LIMITEDโ
Rate limit exceeded.
{
"error": true,
"code": "RATE_LIMITED",
"message": "Rate limit exceeded. Please wait before making more requests.",
"details": {
"retryAfter": 60,
"limit": 100,
"remaining": 0,
"resetAt": "2026-02-09T00:00:00Z",
"limitType": "per_minute",
"tier": "anonymous"
}
}
Response Headersโ
HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1707440400
Limit Typesโ
| Type | Description | Reset Window |
|---|---|---|
per_minute | Burst protection | Rolling 60 seconds |
daily | Daily quota | Rolling 24 hours |
Handlingโ
async function fetchWithRetry(url, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
const response = await fetch(url);
if (response.status === 429) {
const data = await response.json();
const retryAfter = data.details?.retryAfter || 60;
console.log(`Rate limited (${data.details?.limitType}). Waiting ${retryAfter}s...`);
await sleep(retryAfter * 1000);
continue;
}
return response;
}
throw new Error('Max retries exceeded');
}
Relatedโ
500 Internal Server Error โ SERVER_ERRORโ
An unexpected error occurred on the server.
{
"error": true,
"code": "SERVER_ERROR",
"message": "An unexpected error occurred. Please try again.",
"details": {
"requestId": "req_abc123def456",
"timestamp": "2026-02-08T15:30:00Z"
}
}
Handlingโ
try {
const data = await client.fetch('/api/some-endpoint');
} catch (error) {
if (error.code === 'SERVER_ERROR') {
console.error(`Server error. Request ID: ${error.details?.requestId}`);
// Retry with exponential backoff
await retryWithBackoff(() => client.fetch('/api/some-endpoint'));
}
}
Retry Strategyโ
async function retryWithBackoff(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (error.status >= 500 && i < maxRetries - 1) {
const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
console.log(`Retrying in ${delay}ms...`);
await sleep(delay);
continue;
}
throw error;
}
}
}
502 Bad Gateway โ BAD_GATEWAYโ
Upstream service returned an invalid response.
{
"error": true,
"code": "BAD_GATEWAY",
"message": "Upstream service returned an invalid response",
"details": {
"source": "coingecko",
"upstream_status": 500,
"fallback_attempted": true,
"fallback_failed": true
}
}
Common Causesโ
- External API returned malformed data
- External API returned unexpected error
- Network issue between servers
Handlingโ
if (error.code === 'BAD_GATEWAY') {
// Wait and retry - external service issue
await sleep(5000);
return retry();
}
503 Service Unavailable โ SERVICE_UNAVAILABLEโ
Service is temporarily unavailable or overloaded.
{
"error": true,
"code": "SERVICE_UNAVAILABLE",
"message": "Service temporarily unavailable. Please try again later.",
"details": {
"source": "defillama",
"reason": "maintenance",
"estimatedRecovery": "2026-02-08T16:00:00Z",
"alternativeEndpoint": "/api/price/batch"
}
}
Variationsโ
| Reason | Description | Action |
|---|---|---|
maintenance | Scheduled maintenance | Check status page |
overloaded | High traffic | Wait and retry |
source_unavailable | External API down | Use alternative endpoint |
circuit_breaker | Too many failures | Wait for recovery |
Handlingโ
if (error.code === 'SERVICE_UNAVAILABLE') {
const details = error.details;
if (details?.alternativeEndpoint) {
// Try the alternative
return fetch(details.alternativeEndpoint);
}
if (details?.estimatedRecovery) {
const recoveryTime = new Date(details.estimatedRecovery);
const waitMs = recoveryTime - Date.now();
if (waitMs > 0 && waitMs < 300000) { // Max 5 min wait
await sleep(waitMs);
return retry();
}
}
// Exponential backoff
await sleep(30000);
return retry();
}
504 Gateway Timeout โ GATEWAY_TIMEOUTโ
Request took too long to process.
{
"error": true,
"code": "GATEWAY_TIMEOUT",
"message": "Request timed out. Please try again with a smaller request.",
"details": {
"timeoutMs": 30000,
"operation": "batch_resolve",
"itemsRequested": 500,
"suggestion": "Try with fewer items (max 100 recommended)"
}
}
Common Causesโ
| Cause | Solution |
|---|---|
| Large batch request | Reduce batch size |
| Complex query | Simplify query |
| Slow upstream | Retry later |
| Network congestion | Retry with longer timeout |
Handlingโ
if (error.code === 'GATEWAY_TIMEOUT') {
const suggestion = error.details?.suggestion;
if (error.details?.itemsRequested > 100) {
// Split into smaller batches
return batchProcess(items, 50);
}
// Simple retry
await sleep(5000);
return retry();
}
SDK Error Handlingโ
JavaScript/TypeScriptโ
import { Web3IdentityClient, APIError } from '@atv-eth/x402-sdk';
const client = new Web3IdentityClient();
try {
const profile = await client.getProfile('vitalik.eth');
} catch (error) {
if (error instanceof APIError) {
console.log('Status:', error.status); // 404
console.log('Code:', error.code); // "NOT_FOUND"
console.log('Message:', error.message); // "ENS name not found"
console.log('Details:', error.details); // { resource: "ens_name", ... }
switch (error.code) {
case 'NOT_FOUND':
return null;
case 'RATE_LIMITED':
await sleep(error.details.retryAfter * 1000);
return retry();
case 'PAYMENT_REQUIRED':
return handlePayment(error.details);
default:
throw error;
}
}
throw error;
}
Pythonโ
from web3identity import Client, APIError
client = Client()
try:
profile = client.get_profile("vitalik.eth")
except APIError as e:
print(f"Status: {e.status}")
print(f"Code: {e.code}")
print(f"Message: {e.message}")
if e.code == "RATE_LIMITED":
time.sleep(e.details.get("retryAfter", 60))
profile = client.get_profile("vitalik.eth")
elif e.code == "NOT_FOUND":
profile = None
else:
raise
Complete Error Handling Exampleโ
async function robustFetch(url, options = {}, config = {}) {
const { maxRetries = 3, timeout = 30000 } = config;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
const response = await fetch(url, {
...options,
signal: controller.signal,
});
clearTimeout(timeoutId);
if (response.ok) {
return response.json();
}
const error = await response.json();
// Handle retryable errors
if ([429, 500, 502, 503, 504].includes(response.status)) {
if (attempt < maxRetries) {
const delay = response.status === 429
? (error.details?.retryAfter || 60) * 1000
: Math.pow(2, attempt) * 1000;
console.log(`Attempt ${attempt}/${maxRetries} failed. Retrying in ${delay}ms...`);
await sleep(delay);
continue;
}
}
// Handle payment required
if (response.status === 402) {
return handleX402Payment(url, options, error.details);
}
// Non-retryable error
throw new APIError(error, response.status);
} catch (error) {
if (error.name === 'AbortError') {
if (attempt < maxRetries) {
console.log(`Request timed out. Retrying...`);
continue;
}
throw new Error('Request timed out after all retries');
}
throw error;
}
}
}
Relatedโ
- Rate Limits โ Understanding rate limiting
- x402 Payments โ Handling payment required errors
- Authentication โ Avoiding auth errors