Skip to content

Error Handling

Handle errors gracefully and provide clear feedback to users.

Error Response Format

All WalletGate API errors follow this consistent structure:

json
{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable error message",
    "field": "fieldName",  // Optional: which field caused the error
    "details": {           // Optional: additional context
      "key": "value"
    }
  }
}

HTTP Status Codes

CodeMeaningWhen It Happens
200SuccessRequest completed successfully
201CreatedResource created (e.g., new session)
400Bad RequestInvalid input or validation error
401UnauthorizedMissing or invalid API key
403ForbiddenQuota exceeded or insufficient permissions
404Not FoundResource doesn't exist
429Too Many RequestsRate limit exceeded
500Internal Server ErrorSomething went wrong on our end

Common Errors

Authentication Errors

Missing API Key

json
{
  "success": false,
  "error": {
    "code": "MISSING_API_KEY",
    "message": "No API key provided. Include Authorization header with Bearer token."
  }
}

Fix:

typescript
// ✅ Correct
headers: {
  'Authorization': `Bearer ${apiKey}`,
}

// ❌ Wrong - missing header
headers: {}

Invalid API Key

json
{
  "success": false,
  "error": {
    "code": "INVALID_API_KEY",
    "message": "API key is invalid or has been revoked."
  }
}

Causes:

  • Key was deleted
  • Typo in key
  • Key format incorrect

Fix: Verify key in dashboard

Validation Errors

Invalid Check Type

json
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid check type",
    "field": "checks[0].type",
    "details": {
      "allowed": ["age_over", "residency_eu", "identity_verified"]
    }
  }
}

Fix:

typescript
// ✅ Correct
checks: [{ type: 'age_over', value: 18 }]

// ❌ Wrong
checks: [{ type: 'age_check', value: 18 }]

Missing Required Field

json
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "checks is required",
    "field": "checks"
  }
}

Invalid URL Format

json
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid redirect URL",
    "field": "redirectUrl",
    "details": {
      "value": "not-a-url",
      "expected": "Valid HTTPS URL"
    }
  }
}

Quota Errors

Monthly Limit Exceeded

json
{
  "success": false,
  "error": {
    "code": "QUOTA_EXCEEDED",
    "message": "Monthly verification quota exceeded. Upgrade your plan or wait for reset.",
    "details": {
      "used": 1000,
      "limit": 1000,
      "resetAt": "2024-02-01T00:00:00Z",
      "upgradeUrl": "https://walletgate.app/admin"
    }
  }
}

Fix: Upgrade plan or wait for monthly reset

Rate Limit Errors

Too Many Requests

json
{
  "success": false,
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Too many requests. Please slow down.",
    "details": {
      "limit": 10,
      "remaining": 0,
      "resetAt": 1704067260000
    }
  }
}

Headers included:

X-RateLimit-Limit: 10
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1704067260

Fix: Implement exponential backoff

Resource Errors

Session Not Found

json
{
  "success": false,
  "error": {
    "code": "SESSION_NOT_FOUND",
    "message": "Verification session not found or expired."
  }
}

Causes:

  • Session ID doesn't exist
  • Session expired (15 min timeout)
  • Typo in session ID

Verification Errors

User Declined

json
{
  "success": false,
  "error": {
    "code": "USER_DECLINED",
    "message": "User cancelled verification in wallet."
  }
}

Session Expired

json
{
  "success": false,
  "error": {
    "code": "SESSION_EXPIRED",
    "message": "Verification session expired after 15 minutes."
  }
}

Invalid Credentials

json
{
  "success": false,
  "error": {
    "code": "INVALID_CREDENTIALS",
    "message": "Certificate validation failed",
    "details": {
      "reason": "Certificate revoked by issuer",
      "issuer": "DE Trust Service Provider"
    }
  }
}

Error Handling Patterns

Basic Try-Catch

typescript
import { WalletGate } from '@walletgate/eudi';

const client = new WalletGate({
  apiKey: process.env.WALLETGATE_API_KEY,
});

try {
  const session = await client.createSession({
    checks: [{ type: 'age_over', value: 18 }],
  });

  console.log('Session created:', session.id);
} catch (error) {
  if (error.response) {
    // API returned an error response
    const { code, message } = error.response.data.error;

    switch (code) {
      case 'QUOTA_EXCEEDED':
        console.error('Quota exceeded. Upgrade plan.');
        break;

      case 'INVALID_API_KEY':
        console.error('Invalid API key. Check configuration.');
        break;

      default:
        console.error(`API error: ${message}`);
    }
  } else {
    // Network error or other issue
    console.error('Network error:', error.message);
  }
}

Retry with Exponential Backoff

typescript
async function createSessionWithRetry(
  checks: any[],
  maxRetries: number = 3
): Promise<VerificationSession> {
  let lastError: Error;

  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await client.createSession({ checks });
    } catch (error) {
      lastError = error;

      // Don't retry on validation errors or quota errors
      if (error.response?.data?.error?.code === 'VALIDATION_ERROR') {
        throw error;
      }
      if (error.response?.data?.error?.code === 'QUOTA_EXCEEDED') {
        throw error;
      }

      // Exponential backoff
      const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
      console.log(`Retry attempt ${attempt + 1} after ${delay}ms`);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }

  throw lastError;
}

User-Friendly Error Messages

typescript
function getUser FriendlyErrorMessage(error: any): string {
  const errorCode = error.response?.data?.error?.code;

  const messages: Record<string, string> = {
    QUOTA_EXCEEDED: 'We\'ve reached our verification limit. Please try again later or contact support.',
    USER_DECLINED: 'Verification was cancelled. Please try again when you\'re ready.',
    SESSION_EXPIRED: 'Verification link expired. Click below to generate a new one.',
    INVALID_CREDENTIALS: 'We couldn\'t verify your credentials. Please ensure your wallet is up to date.',
    RATE_LIMIT_EXCEEDED: 'Too many attempts. Please wait a moment and try again.',
  };

  return messages[errorCode] || 'Something went wrong. Please try again or contact support.';
}

// Usage in UI
try {
  await createSession();
} catch (error) {
  showErrorToast(getUserFriendlyErrorMessage(error));
}

Frontend Error Handling

React Example

typescript
import { useState } from 'react';

function VerificationButton() {
  const [error, setError] = useState<string | null>(null);
  const [loading, setLoading] = useState(false);

  const handleVerify = async () => {
    setError(null);
    setLoading(true);

    try {
      const response = await fetch('/api/verify/create', {
        method: 'POST',
      });

      if (!response.ok) {
        const data = await response.json();
        throw new Error(data.error.message);
      }

      const { sessionId } = await response.json();
      // Show QR code...
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div>
      <button onClick={handleVerify} disabled={loading}>
        {loading ? 'Creating session...' : 'Verify Age'}
      </button>

      {error && (
        <div className="error-message">
          ⚠️ {error}
        </div>
      )}
    </div>
  );
}

Logging Best Practices

What to Log

typescript
// ✅ Log these
console.error('Verification failed:', {
  sessionId: session.id,
  errorCode: error.code,
  timestamp: new Date().toISOString(),
  userId: metadata.userId, // If applicable
});

// ❌ Don't log these
console.error('Error:', {
  apiKey: process.env.WALLETGATE_API_KEY, // NEVER log API keys!
  rawCredentials: credentials, // NEVER log PII!
});

Structured Logging

typescript
import pino from 'pino';

const logger = pino();

try {
  await client.createSession({ checks });
} catch (error) {
  logger.error({
    msg: 'Session creation failed',
    errorCode: error.response?.data?.error?.code,
    errorMessage: error.response?.data?.error?.message,
    endpoint: '/v1/verify/sessions',
    method: 'POST',
    timestamp: Date.now(),
  });
}

Monitoring & Alerts

Set Up Alerts

Monitor these error rates:

  • High priority:

    • Authentication errors > 5%
    • Internal server errors > 0.1%
    • Quota exceeded (upgrade needed)
  • Medium priority:

    • Validation errors > 10%
    • Rate limit errors > 1%
  • Low priority:

    • User declined > 20%
    • Session expired > 10%

Error Rate Calculation

typescript
const errorRate = (failedRequests / totalRequests) * 100;

if (errorRate > 5) {
  alertTeam({
    message: `High error rate: ${errorRate}%`,
    severity: 'high',
    details: {
      totalRequests,
      failedRequests,
      timeWindow: '5 minutes',
    },
  });
}

Complete Error Code Reference

CodeHTTPDescriptionAction
MISSING_API_KEY401No Authorization headerAdd API key
INVALID_API_KEY401Key invalid or revokedCheck dashboard
QUOTA_EXCEEDED403Monthly limit reachedUpgrade plan
RATE_LIMIT_EXCEEDED429Too many requestsImplement backoff
VALIDATION_ERROR400Invalid inputFix request body
SESSION_NOT_FOUND404Session doesn't existCheck session ID
SESSION_EXPIRED400Session timed out (15min)Create new session
USER_DECLINED400User cancelled in walletRetry
INVALID_CREDENTIALS400Certificate validation failedCheck wallet
INTERNAL_ERROR500Server errorRetry, contact support

View interactive API reference →

Testing Error Scenarios

Force Errors in Test Mode

typescript
// Force validation error
await client.createSession({
  checks: [{ type: 'invalid_type', value: 18 }], // Wrong type
});

// Force session not found
await client.getSession('nonexistent-session-id');

Simulate Network Errors

typescript
// Simulate timeout
const client = new WalletGate({
  apiKey: process.env.WALLETGATE_API_KEY,
  timeout: 1, // 1ms timeout - will always fail
});

// Simulate network error
// (disconnect internet, use invalid base URL, etc.)

Next Steps

Need Help?

Developer-first EU Digital Identity verification