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)
| Code | Status | Description |
|---|---|---|
| 200 | OK | Request successful |
| 201 | Created | Resource successfully created |
Client Error Codes (4xx)
| Code | Status | Description |
|---|---|---|
| 400 | Bad Request | Invalid request parameters or body |
| 401 | Unauthorized | Authentication failed (handled by Auth Server/Dashboard) |
| 404 | Not Found | Resource not found (asset, category, provider) |
| 422 | Unprocessable Entity | Validation errors |
| 429 | Too Many Requests | Rate 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)
| Code | Status | Description |
|---|---|---|
| 500 | Internal Server Error | Unexpected server error |
| 503 | Service Unavailable | External 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 /assetsto list available assets - Create asset using
POST /assetsif 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_KEYin 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)
-
Short-term:
- Serve cached rates with staleness indicator
- Retry with exponential backoff
- Use last known good rate
-
Long-term:
- Add additional rate providers
- Implement circuit breaker pattern
- Set up provider redundancy
For 400 Errors (Bad Request)
- Validate input client-side before sending
- Check asset codes against
GET /assetslist - Ensure all required fields are present
- 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:
- Verify user is logged into Link Dashboard
- Check JWT token validity and expiration
- Ensure session cookies are being sent (
credentials: 'include') - 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:
- Review API documentation and README
- Check JIRA for similar reported issues
- Enable debug logging (
LOG_LEVEL=debug) to investigate - Contact helpdesk@ledgerlink.ai with:
- Timestamp of error
- Full error response
- Steps to reproduce
- Relevant log output
Next: FAQ | API Reference