Error Handling
This guide covers best practices for handling errors in your TraderPal Connect integration.
Error Response Format
All API errors follow a consistent format:
{
"error": {
"code": "invalid_request",
"message": "The request was invalid",
"details": {
"field": "symbol",
"reason": "Invalid stock symbol"
},
"requestId": "req_123456789"
}
}
Common Error Codes
Authentication Errors (4xx)
Code | Description | Resolution |
---|---|---|
401 | Unauthorized | Check API credentials |
403 | Forbidden | Verify permissions |
404 | Not Found | Check resource exists |
429 | Too Many Requests | Implement rate limiting |
Business Logic Errors (4xx)
Code | Description | Resolution |
---|---|---|
400 | Invalid Request | Validate input data |
422 | Unprocessable Entity | Check business rules |
409 | Conflict | Handle race conditions |
412 | Precondition Failed | Check state requirements |
Server Errors (5xx)
Code | Description | Resolution |
---|---|---|
500 | Internal Server Error | Retry with backoff |
502 | Bad Gateway | Check service status |
503 | Service Unavailable | Implement circuit breaker |
504 | Gateway Timeout | Adjust timeout settings |
Error Handling Strategies
Retry Logic
async function retryRequest(fn, maxRetries = 3, delay = 1000) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (!isRetryableError(error) || i === maxRetries - 1) {
throw error;
}
await sleep(delay * Math.pow(2, i));
}
}
}
function isRetryableError(error) {
return [500, 502, 503, 504].includes(error.status);
}
Circuit Breaker
class CircuitBreaker {
constructor(fn, options = {}) {
this.fn = fn;
this.state = 'closed';
this.failureCount = 0;
this.failureThreshold = options.failureThreshold || 5;
this.resetTimeout = options.resetTimeout || 60000;
}
async execute(...args) {
if (this.state === 'open') {
throw new Error('Circuit breaker is open');
}
try {
const result = await this.fn(...args);
this.failureCount = 0;
return result;
} catch (error) {
this.failureCount++;
if (this.failureCount >= this.failureThreshold) {
this.state = 'open';
setTimeout(() => this.reset(), this.resetTimeout);
}
throw error;
}
}
reset() {
this.state = 'closed';
this.failureCount = 0;
}
}
Rate Limiting
class RateLimiter {
constructor(limit, interval) {
this.limit = limit;
this.interval = interval;
this.requests = [];
}
async execute(fn) {
this.clearOldRequests();
if (this.requests.length >= this.limit) {
throw new Error('Rate limit exceeded');
}
this.requests.push(Date.now());
return fn();
}
clearOldRequests() {
const now = Date.now();
this.requests = this.requests.filter(
time => now - time < this.interval
);
}
}
Error Handling Examples
Order Placement
async function placeOrder(orderData) {
const circuitBreaker = new CircuitBreaker(
client.orders.create,
{ failureThreshold: 3 }
);
try {
const order = await retryRequest(
() => circuitBreaker.execute(orderData)
);
return order;
} catch (error) {
if (error.code === 'insufficient_funds') {
// Handle business logic error
throw new BusinessError('Insufficient funds for order');
} else if (error.status === 429) {
// Handle rate limiting
await sleep(error.retryAfter * 1000);
return placeOrder(orderData);
} else {
// Handle unexpected errors
logError(error);
throw error;
}
}
}
User Authentication
async function authenticateUser(credentials) {
try {
const auth = await client.auth.login(credentials);
return auth;
} catch (error) {
switch (error.code) {
case 'invalid_credentials':
throw new AuthError('Invalid username or password');
case 'account_locked':
throw new AuthError('Account locked. Please contact support');
case 'requires_2fa':
return handle2FAFlow(credentials);
default:
logError(error);
throw new Error('Authentication failed');
}
}
}
Logging Best Practices
Error Log Format
function logError(error, context = {}) {
const errorLog = {
timestamp: new Date().toISOString(),
requestId: error.requestId,
code: error.code,
message: error.message,
stack: error.stack,
context: {
userId: context.userId,
action: context.action,
...context
}
};
logger.error(errorLog);
}
Sensitive Data Handling
function sanitizeError(error) {
const sensitiveFields = ['password', 'token', 'key'];
const sanitized = { ...error };
sensitiveFields.forEach(field => {
if (sanitized[field]) {
sanitized[field] = '[REDACTED]';
}
});
return sanitized;
}
Monitoring and Alerts
Error Rate Monitoring
class ErrorMonitor {
constructor(threshold = 0.05) {
this.errors = [];
this.requests = 0;
this.threshold = threshold;
}
trackRequest() {
this.requests++;
this.checkErrorRate();
}
trackError(error) {
this.errors.push({
timestamp: Date.now(),
error
});
this.checkErrorRate();
}
checkErrorRate() {
const errorRate = this.errors.length / this.requests;
if (errorRate > this.threshold) {
alertTeam('High error rate detected');
}
}
}
Best Practices
-
Validate Input Data
- Check required fields
- Validate data types
- Sanitize user input
-
Handle Edge Cases
- Network timeouts
- Service unavailability
- Race conditions
-
Implement Fallbacks
- Cache responses
- Default values
- Graceful degradation
-
Monitor and Alert
- Track error rates
- Set up alerts
- Monitor response times