Skip to main content

Error Handling

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

Standard Error Response Format

All error responses follow a consistent structure:

{
"statusCode": 404,
"message": "Transaction with ID 550e8400-e29b-41d4-a716-446655440000 not found",
"error": "Not Found",
"timestamp": "2025-11-20T17:00:00.000Z",
"path": "/transactions/550e8400-e29b-41d4-a716-446655440000"
}

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 (transaction, asset, etc.)
409ConflictResource conflict (duplicate transaction, etc.)
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
  • 403 (Forbidden) is not currently implemented (no RBAC/permissions)
  • Link Core service itself returns: 400, 404, 409, 422, and 5xx errors

Server Error Codes (5xx)

CodeStatusDescription
500Internal Server ErrorUnexpected server error
502Bad GatewayUpstream service error
503Service UnavailableService temporarily unavailable
504Gateway TimeoutRequest timeout

Common Error Scenarios

1. Transaction Not Found

Scenario: Requesting a non-existent transaction

GET /transactions/invalid-id

Error Response:

{
"statusCode": 404,
"message": "Transaction with ID invalid-id not found",
"error": "Not Found"
}

Solution:

  • Verify transaction ID is correct
  • Check if transaction exists using GET /transactions

2. Invalid Withdrawal Request

Scenario: Missing required fields or invalid data

POST /transactions/withdrawal
{
"customerId": "customer-123"
// Missing required fields
}

Error Response:

{
"statusCode": 400,
"message": ["assetId should not be empty", "amount should not be empty"],
"error": "Bad Request"
}

Solution:

  • Include all required fields (customerId, assetId, amount, destinationAddress)
  • Validate data types and formats before sending

3. Insufficient Balance

Scenario: Withdrawal amount exceeds available balance

Error Response:

{
"statusCode": 400,
"message": "Insufficient balance for withdrawal",
"error": "Bad Request"
}

Solution:

  • Check customer balance before requesting withdrawal
  • Ensure amount doesn't exceed available balance

4. Invalid Transaction Status

Scenario: Attempting operation on transaction in wrong status

POST /transactions/:id/approve-frozen-deposit
# Transaction is not in FROZEN status

Error Response:

{
"statusCode": 409,
"message": "Transaction is not in FROZEN status",
"error": "Conflict"
}

Solution:

  • Check transaction status before performing operations
  • Only approve/reject transactions in FROZEN status

5. Asset Not Found

Scenario: Requesting withdrawal with non-existent asset

Error Response:

{
"statusCode": 404,
"message": "Asset with ID asset-123 not found",
"error": "Not Found"
}

Solution:

  • Verify asset ID exists using GET /assets
  • Create asset first if needed

6. Validation Errors

Scenario: Invalid data format

POST /transactions/withdrawal
{
"customerId": "customer-123",
"assetId": "btc",
"amount": "invalid-amount",
"destinationAddress": "invalid-address"
}

Error Response:

{
"statusCode": 422,
"message": [
"amount must be a valid number",
"destinationAddress must be a valid blockchain address"
],
"error": "Unprocessable Entity"
}

Solution:

  • Validate all input data before sending
  • Use proper data types and formats

Best Practices

1. Implement Retry Logic

async function fetchTransactionWithRetry(
id: string,
maxRetries = 3
): Promise<Transaction> {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(`/api/link-core/v1/transactions/${id}`);

if (!response.ok) {
if (response.status === 404) {
throw new Error('Transaction not found');
}

// Retry on 5xx errors
if (response.status >= 500 && i < maxRetries - 1) {
await sleep(Math.pow(2, i) * 1000); // Exponential backoff
continue;
}
}

return response.json();
} catch (error) {
if (i === maxRetries - 1) throw error;
}
}
}

2. Validate Before Sending

function validateWithdrawalRequest(request: WithdrawalRequest): void {
if (!request.customerId) {
throw new Error('Customer ID is required');
}

if (!request.assetId) {
throw new Error('Asset ID is required');
}

if (!request.amount || parseFloat(request.amount) <= 0) {
throw new Error('Amount must be greater than zero');
}

if (!request.destinationAddress) {
throw new Error('Destination address is required');
}
}

3. Handle Transaction States

async function handleTransaction(transactionId: string) {
const transaction = await fetchTransaction(transactionId);

switch (transaction.status) {
case 'PENDING':
console.log('Transaction is pending screening');
break;

case 'SCREENING':
console.log('Transaction is being screened');
break;

case 'FROZEN':
console.log('Transaction requires manual review');
// Notify admin for review
break;

case 'APPROVED':
console.log('Transaction approved, processing...');
break;

case 'COMPLETED':
console.log('Transaction completed');
break;

case 'REJECTED':
console.error('Transaction rejected');
// Notify customer
break;

case 'FAILED':
console.error('Transaction failed');
// Handle failure
break;
}
}

4. Monitor and Alert

  • Track error rates by status code and endpoint
  • Alert on sustained 5xx errors
  • Monitor transaction failure rates
  • Log errors with full context for debugging
  • Set up health checks for critical flows

Recovery Strategies

For 5xx Errors (Server Errors)

  1. Short-term:

    • Retry with exponential backoff
    • Queue failed requests for later retry
    • Show user-friendly error messages
  2. Long-term:

    • Implement circuit breaker pattern
    • Set up fallback mechanisms
    • Monitor and alert on error spikes

For 400 Errors (Bad Request)

  1. Implement client-side validation
  2. Provide clear error messages to users
  3. Log validation failures for analysis
  4. Update UI to prevent invalid inputs

For 404 Errors (Not Found)

  1. Verify resource IDs before operations
  2. Handle deleted/non-existent resources gracefully
  3. Provide helpful suggestions (e.g., "Transaction may have been deleted")

For 409 Errors (Conflict)

  1. Check resource state before operations
  2. Handle state transitions properly
  3. Implement optimistic locking where needed
  4. Retry with updated state

Debug Mode

Enable detailed logging in local development:

# In .env
NODE_ENV=development
LOG_LEVEL=debug

This will show:

  • Database queries
  • External service calls
  • Transaction state changes
  • Queue job processing
  • Detailed error stack traces

Support

If errors persist:

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

Next: FAQ | API Reference