Skip to main content

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": {}
}
FieldTypeDescription
errorbooleanAlways true for errors
codestringMachine-readable error code
messagestringHuman-readable description
detailsobjectAdditional context (optional)

HTTP Status Code Referenceโ€‹

CodeNameError CodeDescription
400Bad RequestINVALID_PARAMSInvalid request parameters
401UnauthorizedAUTH_REQUIREDAuthentication required or invalid
402Payment RequiredPAYMENT_REQUIREDx402 payment needed
403ForbiddenFORBIDDENAccess denied to resource
404Not FoundNOT_FOUNDResource doesn't exist
429Too Many RequestsRATE_LIMITEDRate limit exceeded
500Internal Server ErrorSERVER_ERRORUnexpected server error
502Bad GatewayBAD_GATEWAYUpstream service error
503Service UnavailableSERVICE_UNAVAILABLEService temporarily down
504Gateway TimeoutGATEWAY_TIMEOUTUpstream 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โ€‹

ErrorExampleFix
Invalid address0xinvalidUse valid 0x + 40 hex chars
Invalid ENS nametest..ethUse valid ENS format
Missing required param{}Include all required fields
Invalid JSON{name:}Fix JSON syntax
Invalid chain IDchainId: 999999Use 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โ€‹

ReasonMessageSolution
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โ€‹

  1. Receive 402 with payment details
  2. Sign EIP-712 payment authorization
  3. Include payment header in retry request
  4. 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;
}

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โ€‹

ReasonDescription
insufficient_permissionsYour account lacks required permissions
ip_blockedYour IP has been blocked
endpoint_restrictedEndpoint requires special access
resource_privateResource 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โ€‹

CodeResourceDescription
ENS_NOT_FOUNDENS nameName not registered
ADDRESS_NOT_FOUNDAddressNo data for this address
TOKEN_NOT_FOUNDTokenToken not in our database
CHAIN_NOT_FOUNDChainUnsupported chain ID
ENDPOINT_NOT_FOUNDEndpointInvalid 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โ€‹

TypeDescriptionReset Window
per_minuteBurst protectionRolling 60 seconds
dailyDaily quotaRolling 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');
}

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โ€‹

ReasonDescriptionAction
maintenanceScheduled maintenanceCheck status page
overloadedHigh trafficWait and retry
source_unavailableExternal API downUse alternative endpoint
circuit_breakerToo many failuresWait 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โ€‹

CauseSolution
Large batch requestReduce batch size
Complex querySimplify query
Slow upstreamRetry later
Network congestionRetry 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;
}
}
}