❌ Error Handling & Troubleshooting Guide
📋 Overview
This guide provides comprehensive error handling strategies for Partner Services API integration, including common error scenarios, troubleshooting steps, and best practices for robust error management.
Coverage:
- ✅ Authentication Errors - HMAC, Partner ID, Timestamp issues
- ✅ API Errors - Rate limits, validation, service unavailable
- ✅ Network Errors - Timeouts, connectivity, DNS issues
- ✅ Business Logic Errors - Insufficient coverage, invalid data
- ✅ Recovery Strategies - Retry logic, fallback mechanisms
🔍 Error Categories
1. Authentication Errors (401)
AUTHENTICATION_FAILED - Invalid HMAC signature
json
{
"success": false,
"error": {
"code": "AUTHENTICATION_FAILED",
"message": "Invalid HMAC signature or partner credentials",
"details": {
"reason": "SIGNATURE_MISMATCH",
"partnerId": "partner_001",
"timestamp": 1642752480
}
}
}
Common Causes:
- Incorrect secret key
- Wrong string-to-sign format
- JSON formatting inconsistency
- Extra whitespace in request body
Solutions:
javascript
// Debug signature generation
function debugSignature(method, path, body, partnerId, secretKey) {
const timestamp = Math.floor(Date.now() / 1000);
const bodyStr = body ? JSON.stringify(body) : "";
const stringToSign = `${method}\n${path}\n${bodyStr}\n${timestamp}`;
console.log("Debug Info:");
console.log("String to Sign:", JSON.stringify(stringToSign));
console.log("Body String:", bodyStr);
console.log("Secret Key Length:", secretKey.length);
const signature = crypto
.createHmac("sha256", secretKey)
.update(stringToSign)
.digest("hex");
return signature;
}
PARTNER_NOT_FOUND
json
{
"success": false,
"error": {
"code": "PARTNER_NOT_FOUND",
"message": "Partner ID not found or inactive"
}
}
Solutions:
- Verify partner ID is correct
- Check partner account status
- Ensure API access is enabled
TIMESTAMP_INVALID
json
{
"success": false,
"error": {
"code": "TIMESTAMP_INVALID",
"message": "Request timestamp is too old or too far in the future"
}
}
Solutions:
javascript
// Ensure timestamp is current
const timestamp = Math.floor(Date.now() / 1000);
// Check system clock synchronization
const serverTime = await getServerTime();
const clockDrift = Math.abs(Date.now() - serverTime);
if (clockDrift > 30000) {
// 30 seconds
console.warn("System clock may be out of sync");
}
2. Rate Limiting Errors (429)
RATE_LIMIT_EXCEEDED
json
{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Request rate limit exceeded for your tier",
"details": {
"tier": "TIER_1",
"limit": 1000,
"window": "60 seconds",
"resetTime": "2025-07-22T10:49:00.000Z",
"retryAfter": 15
}
}
}
Rate Limit Headers:
http
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1642752540
Retry-After: 15
Handling Strategy:
javascript
class RateLimitHandler {
async makeRequestWithRetry(requestFn, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await requestFn();
} catch (error) {
if (error.code === "RATE_LIMIT_EXCEEDED") {
const retryAfter = error.details?.retryAfter || attempt * 2;
if (attempt < maxRetries) {
console.log(`Rate limited. Retrying in ${retryAfter} seconds...`);
await this.sleep(retryAfter * 1000);
continue;
} else {
throw new Error("Rate limit exceeded. Max retries reached.");
}
}
throw error; // Re-throw non-rate-limit errors
}
}
}
sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
}
3. Validation Errors (400)
VALIDATION_ERROR
json
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": {
"validationErrors": [
{
"field": "pickupPincode",
"value": "invalid",
"issue": "Must be a valid 6-digit pincode",
"expectedFormat": "NNNNNN (6 digits)"
},
{
"field": "weight",
"value": -100,
"issue": "Weight must be greater than 0",
"constraints": {
"min": 1,
"max": 50000,
"unit": "grams"
}
}
]
}
}
}
Validation Helper:
javascript
class ShipmentValidator {
static validate(shipmentDetails) {
const errors = [];
// Pincode validation
if (
!shipmentDetails.pickupPincode ||
!/^\d{6}$/.test(shipmentDetails.pickupPincode)
) {
errors.push({
field: "pickupPincode",
message: "Must be a valid 6-digit pincode",
});
}
if (
!shipmentDetails.deliveryPincode ||
!/^\d{6}$/.test(shipmentDetails.deliveryPincode)
) {
errors.push({
field: "deliveryPincode",
message: "Must be a valid 6-digit pincode",
});
}
// Weight validation
if (
!shipmentDetails.weight ||
shipmentDetails.weight <= 0 ||
shipmentDetails.weight > 50000
) {
errors.push({
field: "weight",
message: "Weight must be between 1g and 50kg",
});
}
// Shipment type validation
const validTypes = ["B2B", "B2C", "C2C"];
if (
!shipmentDetails.shipmentType ||
!validTypes.includes(shipmentDetails.shipmentType)
) {
errors.push({
field: "shipmentType",
message: `Must be one of: ${validTypes.join(", ")}`,
});
}
return {
isValid: errors.length === 0,
errors,
};
}
}
// Usage
const validation = ShipmentValidator.validate(shipmentDetails);
if (!validation.isValid) {
throw new ValidationError("Invalid shipment details", validation.errors);
}
4. Service Unavailable Errors (422/503)
INSUFFICIENT_COVERAGE
json
{
"success": false,
"error": {
"code": "INSUFFICIENT_COVERAGE",
"message": "Partner does not have coverage for requested route",
"details": {
"partnerId": "partner_001",
"pickupPincode": "110001",
"deliveryPincode": "999999",
"coverage": {
"pickup": {
"available": true,
"zone": "ZN001"
},
"delivery": {
"available": false,
"reason": "PINCODE_NOT_SERVICEABLE",
"suggestedAlternatives": ["999998", "999997"]
}
}
}
}
}
Handling Strategy:
javascript
class CoverageHandler {
async findAlternativePartners(shipmentDetails, originalError) {
try {
// Try to find other partners
const alternatives = await this.searchAlternativePartners(
shipmentDetails
);
if (alternatives.length > 0) {
return {
success: true,
alternatives,
message: `Found ${alternatives.length} alternative partners`,
};
}
// Check for nearby pincodes
const nearbyOptions = await this.findNearbyServiceablePincodes(
shipmentDetails.deliveryPincode
);
return {
success: false,
suggestions: nearbyOptions,
originalError,
};
} catch (error) {
return {
success: false,
message: "No alternatives found",
originalError,
};
}
}
}
SERVICE_TEMPORARILY_UNAVAILABLE
json
{
"success": false,
"error": {
"code": "SERVICE_TEMPORARILY_UNAVAILABLE",
"message": "Service is temporarily unavailable",
"details": {
"retryAfter": 300,
"estimatedRecovery": "2025-07-22T11:00:00.000Z"
}
}
}
5. Network Errors
Connection Timeout
javascript
class NetworkErrorHandler {
async makeRequestWithTimeout(requestFn, timeoutMs = 30000) {
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error("Request timeout")), timeoutMs);
});
try {
return await Promise.race([requestFn(), timeoutPromise]);
} catch (error) {
if (error.message === "Request timeout") {
throw new NetworkError("Request timed out", { timeout: timeoutMs });
}
throw error;
}
}
}
DNS Resolution Failure
javascript
// Fallback URL strategy
class URLFailoverHandler {
constructor(baseURLs) {
this.baseURLs = baseURLs;
this.currentIndex = 0;
}
async makeRequest(path, options = {}) {
let lastError;
for (let i = 0; i < this.baseURLs.length; i++) {
const url = this.baseURLs[this.currentIndex];
try {
const response = await fetch(`${url}${path}`, options);
return response;
} catch (error) {
lastError = error;
console.warn(`Request failed for ${url}: ${error.message}`);
// Try next URL
this.currentIndex = (this.currentIndex + 1) % this.baseURLs.length;
}
}
throw new NetworkError("All endpoints failed", { lastError });
}
}
🔄 Comprehensive Error Handler
Universal Error Handler Implementation
javascript
class PartnerServicesErrorHandler {
constructor(options = {}) {
this.maxRetries = options.maxRetries || 3;
this.baseDelay = options.baseDelay || 1000;
this.maxDelay = options.maxDelay || 30000;
this.retryableErrors = options.retryableErrors || [
"RATE_LIMIT_EXCEEDED",
"SERVICE_TEMPORARILY_UNAVAILABLE",
"NETWORK_ERROR",
"TIMEOUT",
];
}
async executeWithErrorHandling(operation, context = {}) {
let lastError;
let attempt = 0;
while (attempt <= this.maxRetries) {
try {
return await operation();
} catch (error) {
lastError = error;
attempt++;
console.error(`Attempt ${attempt} failed:`, {
error: error.message,
code: error.code,
context,
});
if (attempt > this.maxRetries) {
break;
}
if (!this.isRetryableError(error)) {
break;
}
const delay = this.calculateDelay(attempt, error);
console.log(`Retrying in ${delay}ms...`);
await this.sleep(delay);
}
}
// All retries exhausted
throw this.createFinalError(lastError, attempt - 1, context);
}
isRetryableError(error) {
return (
this.retryableErrors.includes(error.code) ||
error.name === "NetworkError" ||
error.name === "TimeoutError"
);
}
calculateDelay(attempt, error) {
// Exponential backoff with jitter
let delay = Math.min(
this.baseDelay * Math.pow(2, attempt - 1),
this.maxDelay
);
// Add jitter (±25%)
const jitter = delay * 0.25 * (Math.random() - 0.5);
delay += jitter;
// Respect server retry-after header
if (error.details?.retryAfter) {
delay = Math.max(delay, error.details.retryAfter * 1000);
}
return Math.round(delay);
}
createFinalError(originalError, attempts, context) {
return new Error(
`Operation failed after ${attempts} retries. ` +
`Last error: ${originalError.message}`,
{
cause: originalError,
attempts,
context,
}
);
}
sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
}
// Usage
const errorHandler = new PartnerServicesErrorHandler({
maxRetries: 3,
baseDelay: 1000,
});
const result = await errorHandler.executeWithErrorHandling(
() =>
client.makeRequest("POST", "/shipments/calculate-charges", shipmentData),
{ operation: "calculate_charges", shipmentId: "SHP_001" }
);
🛠️ Error Recovery Strategies
1. Circuit Breaker Pattern
javascript
class CircuitBreaker {
constructor(options = {}) {
this.threshold = options.threshold || 5;
this.timeout = options.timeout || 60000;
this.monitor = options.monitor || false;
this.state = "CLOSED";
this.failureCount = 0;
this.lastFailure = null;
this.successCount = 0;
}
async execute(operation) {
if (this.state === "OPEN") {
if (Date.now() - this.lastFailure < this.timeout) {
throw new Error("Circuit breaker is OPEN");
} else {
this.state = "HALF_OPEN";
}
}
try {
const result = await operation();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
onSuccess() {
this.failureCount = 0;
this.successCount++;
if (this.state === "HALF_OPEN") {
this.state = "CLOSED";
}
}
onFailure() {
this.failureCount++;
this.lastFailure = Date.now();
if (this.failureCount >= this.threshold) {
this.state = "OPEN";
}
}
}
2. Fallback Data Strategy
javascript
class FallbackDataProvider {
constructor() {
this.cache = new Map();
this.fallbackData = new Map();
}
async getPartnerDataWithFallback(partnerId) {
try {
// Try primary API
const data = await client.makeRequest(
"GET",
`/partners/comprehensive-data`
);
this.cache.set(partnerId, data);
return data;
} catch (error) {
console.warn("Primary API failed, trying cache...");
// Try cache
const cachedData = this.cache.get(partnerId);
if (cachedData) {
return cachedData;
}
// Use fallback data
console.warn("Cache miss, using fallback data...");
const fallbackData = this.fallbackData.get(partnerId);
if (fallbackData) {
return fallbackData;
}
throw new Error(
"No data available (primary API, cache, and fallback all failed)"
);
}
}
setFallbackData(partnerId, data) {
this.fallbackData.set(partnerId, data);
}
}
📊 Error Monitoring & Logging
Error Logger Implementation
javascript
class ErrorLogger {
constructor(options = {}) {
this.logLevel = options.logLevel || "ERROR";
this.enableConsole = options.enableConsole !== false;
this.enableRemote = options.enableRemote || false;
this.remoteEndpoint = options.remoteEndpoint;
}
async logError(error, context = {}) {
const errorLog = {
timestamp: new Date().toISOString(),
level: "ERROR",
message: error.message,
code: error.code,
stack: error.stack,
context,
environment: process.env.NODE_ENV,
userId: context.userId,
sessionId: context.sessionId,
requestId: context.requestId,
};
if (this.enableConsole) {
console.error("API Error:", errorLog);
}
if (this.enableRemote && this.remoteEndpoint) {
try {
await fetch(this.remoteEndpoint, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(errorLog),
});
} catch (logError) {
console.error("Failed to send error log:", logError);
}
}
}
async logWarning(message, context = {}) {
if (this.enableConsole) {
console.warn("API Warning:", {
message,
context,
timestamp: new Date().toISOString(),
});
}
}
}
🎯 Testing Error Scenarios
Error Simulation for Testing
javascript
class ErrorSimulator {
constructor(client) {
this.client = client;
this.errorScenarios = new Map();
}
simulateAuthenticationError() {
// Simulate invalid signature
const originalMakeRequest = this.client.makeRequest;
this.client.makeRequest = async (method, path, body) => {
throw new Error("AUTHENTICATION_FAILED: Invalid HMAC signature");
};
}
simulateRateLimit() {
let requestCount = 0;
const originalMakeRequest = this.client.makeRequest;
this.client.makeRequest = async (method, path, body) => {
requestCount++;
if (requestCount > 5) {
const error = new Error("RATE_LIMIT_EXCEEDED");
error.details = { retryAfter: 10 };
throw error;
}
return originalMakeRequest.call(this.client, method, path, body);
};
}
simulateNetworkTimeout() {
const originalMakeRequest = this.client.makeRequest;
this.client.makeRequest = async (method, path, body) => {
await new Promise((resolve) => setTimeout(resolve, 35000)); // 35 second delay
return originalMakeRequest.call(this.client, method, path, body);
};
}
restoreNormalOperation() {
// Reset to original behavior
delete this.client.makeRequest;
}
}
// Usage in tests
const simulator = new ErrorSimulator(client);
simulator.simulateRateLimit();
// Test error handling
try {
for (let i = 0; i < 10; i++) {
await client.makeRequest("GET", "/health");
}
} catch (error) {
console.log("Expected rate limit error:", error.message);
}
simulator.restoreNormalOperation();
🚨 Emergency Response Procedures
Service Degradation Response
javascript
class EmergencyResponseHandler {
constructor() {
this.degradationMode = false;
this.essentialEndpoints = ["/health", "/partners/availability/check"];
}
enableDegradationMode() {
this.degradationMode = true;
console.warn("🚨 Emergency: Service degradation mode enabled");
}
async handleRequest(method, path, body) {
if (this.degradationMode) {
// Only allow essential endpoints
if (!this.isEssentialEndpoint(path)) {
throw new Error(
"Service temporarily unavailable. Only essential operations allowed."
);
}
// Use cached data when possible
const cachedResponse = await this.getCachedResponse(method, path, body);
if (cachedResponse) {
console.warn("Using cached response due to service degradation");
return cachedResponse;
}
}
return await this.client.makeRequest(method, path, body);
}
isEssentialEndpoint(path) {
return this.essentialEndpoints.some((endpoint) =>
path.startsWith(endpoint)
);
}
}
📋 Error Handling Checklist
Implementation Checklist
[ ] Authentication Error Handling
- [ ] HMAC signature validation
- [ ] Partner ID verification
- [ ] Timestamp validation
- [ ] Secret key rotation support
[ ] Rate Limiting
- [ ] Respect retry-after headers
- [ ] Exponential backoff implementation
- [ ] Request queuing for burst scenarios
[ ] Network Resilience
- [ ] Connection timeout handling
- [ ] DNS failover support
- [ ] Circuit breaker implementation
[ ] Business Logic Errors
- [ ] Coverage validation
- [ ] Alternative partner suggestions
- [ ] Graceful degradation strategies
[ ] Monitoring & Alerting
- [ ] Error rate monitoring
- [ ] Response time tracking
- [ ] Critical error alerting
🎯 Next Steps
- Authentication Guide - Prevent auth errors
- Main System Flow - Robust implementation
- API Examples - Error handling examples
Robust error handling ensures your integration remains stable and provides excellent user experience even when issues occur! ❌→✅