Skip to main content

Error Handling

This guide explains how to handle errors when integrating with the Link Quote API.

Standard Error Response Format

All error responses follow a consistent structure:

{
"statusCode": 400,
"message": "Asset with code BTC not found",
"error": "Bad Request",
"timestamp": "2025-11-20T17:00:00.000Z",
"path": "/v1/rates"
}

HTTP Status Codes

Success Codes (2xx)

CodeStatusDescription
200OKRequest successful
201CreatedResource successfully created

Client Error Codes (4xx)

CodeStatusDescription
400Bad RequestInvalid request parameters or body
401UnauthorizedAuthentication failed (handled by Auth Server/Dashboard)
404Not FoundResource not found (asset, category, provider)
422Unprocessable EntityValidation errors
429Too Many RequestsRate limit exceeded (handled by Auth Server/Dashboard)

Note:

  • Authentication (401) and Rate Limiting (429) are handled by the Link Dashboard's Auth Server before requests reach Link Quote
  • 403 (Forbidden) is not currently implemented (no RBAC)
  • Link Quote service itself returns: 400, 404, 422, and 5xx errors

Server Error Codes (5xx)

CodeStatusDescription
500Internal Server ErrorUnexpected server error
503Service UnavailableExternal provider(s) unavailable or all providers failed

Common Error Scenarios

1. Asset Not Found

Scenario: Requesting a rate for a non-existent asset

POST /v1/rates
{
"assetCode": "INVALID"
}

Error Response:

{
"statusCode": 400,
"message": "Asset with code INVALID not found",
"error": "Bad Request"
}

Solution:

  • Verify asset code is correct
  • Use GET /assets to list available assets
  • Create asset using POST /assets if needed

2. No Providers Available

Scenario: Asset has no configured providers

POST /v1/rates
{
"assetCode": "SOL"
}

Error Response:

{
"statusCode": 503,
"message": "No providers available for this asset",
"error": "Service Unavailable"
}

Solution:

  • Configure providers for the asset: POST /asset-providers
  • Configure providers for the asset's category: POST /category-providers
  • Ensure CoinMarketCap/Pyth providers are active

3. All Providers Failed

Scenario: All external providers returned errors

{
"statusCode": 503,
"message": "Failed to fetch rates from all providers",
"error": "Service Unavailable",
"details": {
"coinmarketcap": "API rate limit exceeded",
"pyth": "Network timeout"
}
}

Solution:

  • Implement retry logic with exponential backoff
  • Check provider API keys and configuration
  • Monitor provider status dashboards
  • Cache recent rates for fallback

4. Invalid UUID

Scenario: Invalid ID format in request

GET /v1/assets/invalid-id

Error Response:

{
"statusCode": 400,
"message": "Validation failed (uuid is expected)",
"error": "Bad Request"
}

Solution: Use valid UUID v4 format (e.g., 550e8400-e29b-41d4-a716-446655440000)

5. Duplicate Asset Code

Scenario: Creating asset with existing code

POST /v1/assets
{
"name": "Bitcoin",
"code": "BTC",
"categoryId": "..."
}

Error Response:

{
"statusCode": 400,
"message": "Asset with code BTC already exists",
"error": "Bad Request"
}

Solution: Use unique asset codes or update existing asset

6. Missing Required Fields

Scenario: Incomplete request body

POST /v1/assets
{
"name": "Ethereum"
}

Error Response:

{
"statusCode": 400,
"message": ["code should not be empty", "categoryId must be a UUID"],
"error": "Bad Request"
}

Solution: Include all required fields per API documentation

7. Invalid Category Reference

Scenario: Asset references non-existent category

POST /v1/assets
{
"name": "Ethereum",
"code": "ETH",
"categoryId": "00000000-0000-0000-0000-000000000000"
}

Error Response:

{
"statusCode": 404,
"message": "Category with ID 00000000-0000-0000-0000-000000000000 not found",
"error": "Not Found"
}

Solution: Create category first or use existing category ID

8. Provider API Key Issues

Scenario: CoinMarketCap API key invalid/expired

Symptoms:

  • 503 errors for all rate requests
  • Log messages: "CoinMarketCap API error: 401 Unauthorized"

Solution:

  • Verify COINMARKETCAP_API_KEY in environment variables
  • Check API key validity and subscription status
  • Monitor CoinMarketCap API usage limits

Best Practices

1. Implement Retry Logic

async function getRateWithRetry(
assetCode: string,
maxRetries = 3
): Promise<Rate> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fetchRate(assetCode);
} catch (error) {
if (error.statusCode === 503 && i < maxRetries - 1) {
// Exponential backoff
await sleep(Math.pow(2, i) * 1000);
continue;
}
throw error;
}
}
}

2. Cache Rates Locally

const rateCache = new Map<string, CachedRate>();

async function getCachedRate(assetCode: string): Promise<number> {
const cached = rateCache.get(assetCode);

// Use cached rate if less than 1 minute old
if (cached && Date.now() - cached.timestamp < 60000) {
return cached.rate;
}

try {
const fresh = await fetchRate(assetCode);
rateCache.set(assetCode, {
rate: fresh.rate,
timestamp: Date.now()
});
return fresh.rate;
} catch (error) {
// Fallback to stale cache if available
if (cached) return cached.rate;
throw error;
}
}

3. Handle Provider Failures Gracefully

async function displayAssetPrice(assetCode: string) {
try {
const rate = await fetchRate(assetCode);
updateUI(rate);
} catch (error) {
if (error.statusCode === 503) {
// Show cached or placeholder data
showUnavailableMessage();
// Schedule retry
setTimeout(() => displayAssetPrice(assetCode), 30000);
} else {
showErrorMessage(error.message);
}
}
}

4. Validate Before Sending

function validateRateRequest(assetCode: string): void {
if (!assetCode || assetCode.trim().length === 0) {
throw new Error('Asset code is required');
}

if (assetCode.length > 10) {
throw new Error('Asset code too long');
}

if (!/^[A-Z0-9]+$/.test(assetCode)) {
throw new Error('Asset code must be alphanumeric uppercase');
}
}

5. Monitor and Alert

  • Track error rates by status code
  • Alert on sustained 503 errors (provider issues)
  • Monitor response times
  • Log failed provider responses for debugging
  • Set up health checks for critical assets

Recovery Strategies

For 503 Errors (Provider Unavailable)

  1. Short-term:

    • Serve cached rates with staleness indicator
    • Retry with exponential backoff
    • Use last known good rate
  2. Long-term:

    • Add additional rate providers
    • Implement circuit breaker pattern
    • Set up provider redundancy

For 400 Errors (Bad Request)

  1. Validate input client-side before sending
  2. Check asset codes against GET /assets list
  3. Ensure all required fields are present
  4. Use proper UUID format for IDs

For 401/403 Errors (Authentication/Authorization)

Authentication errors occur at the Link Dashboard/Auth Server level, not Link Quote:

If you receive 401 errors:

  1. Verify user is logged into Link Dashboard
  2. Check JWT token validity and expiration
  3. Ensure session cookies are being sent (credentials: 'include')
  4. Verify Auth Server is running and accessible

If you receive 403 errors:

  • Note: 403 errors are unlikely as RBAC is not currently implemented
  • All authenticated users have full access to all endpoints
  • If you do receive 403, it may be from network-level restrictions or misconfiguration

For Direct Service Access (Development):

  • Link Quote service doesn't return 401/403 errors
  • Implement network-level security instead

Debug Mode

Enable detailed logging in local development:

# In server/.env
LOG_LEVEL=debug

This will show:

  • Provider request/response details
  • Rate calculation steps
  • Database queries
  • ActiveMQ message publishing

Support

If errors persist:

  1. Review API documentation and README
  2. Check JIRA for similar reported issues
  3. Enable debug logging (LOG_LEVEL=debug) to investigate
  4. Contact helpdesk@ledgerlink.ai with:
    • Timestamp of error
    • Full error response
    • Steps to reproduce
    • Relevant log output

Next: FAQ | API Reference