Skip to content
Playground

Error Handling

Understanding how the WhatsApp Cloud API reports errors is essential for building reliable integrations. This page covers the error response format, common error codes, classification strategies, and retry patterns.

Official Reference: WhatsApp Cloud API Error Codes

Error Response Format

All errors from the WhatsApp Cloud API follow a consistent JSON structure:

{
"error": {
"message": "Message failed to send because more than 24 hours have passed since the customer last replied.",
"type": "OAuthException",
"code": 131047,
"error_subcode": 2388001,
"fbtrace_id": "ABC123XYZ456"
}
}
FieldDescription
messageHuman-readable description of the error
typeError type, usually OAuthException
codePrimary error code
error_subcodeMore specific sub-code (not always present)
fbtrace_idTrace ID for Meta support requests

Common Error Codes

Authentication Errors

CodeDescriptionResolution
100Invalid parameter / missing access tokenCheck that the access token is set and valid
190Access token has expiredRefresh or rotate the token (see Authentication)

Rate Limiting

CodeDescriptionResolution
130429Rate limit hitBack off and retry with exponential delay
4Application-level throttlingReduce request frequency

Message Errors

CodeSubcodeDescriptionResolution
131047238800124-hour messaging window expiredSend a template message to re-open the window
1310312388003Message failed to sendVerify the recipient phone number
131053Template does not existCreate the template in Meta Business Suite first
133016131051Unsupported message typeCheck API version compatibility

Media Errors

CodeSubcodeDescriptionResolution
1310262388082Media upload failedCheck file format, size (max 16 MB for most types), and URL accessibility

Server Errors

CodeDescriptionResolution
500+Internal server error on Meta’s sideRetry with backoff — these are transient

Classifying Errors

Not all errors should be treated the same. Categorizing errors helps you decide whether to retry, alert, or take corrective action:

function classifyError(code: number, subcode?: number) {
// Authentication -- do not retry, fix credentials
if (code === 100 || code === 190) return 'authentication';
// Rate limiting -- retry after backoff
if (code === 130429 || code === 4) return 'rate_limit';
// Business policy -- do not retry, change approach
if (code === 131047) return 'business_policy';
// Invalid input -- do not retry, fix the request
if (code === 131031 || code === 131051 || code === 131053) return 'invalid_input';
// Media errors -- may be retryable
if (code === 131026) return 'media_error';
// Server errors -- retry with backoff
if (code >= 500) return 'server_error';
return 'unknown';
}

The key distinction: retryable errors (rate limits, server errors, transient network issues) should trigger automatic retries, while non-retryable errors (authentication, invalid input, policy violations) need human or programmatic correction.

Basic Error Handling

Wrap SDK calls in try/catch and inspect the error response:

import WhatsApp from 'meta-cloud-api';
const client = new WhatsApp({
accessToken: process.env.CLOUD_API_ACCESS_TOKEN!,
phoneNumberId: Number(process.env.WA_PHONE_NUMBER_ID),
businessAcctId: process.env.WA_BUSINESS_ACCOUNT_ID,
});
try {
const response = await client.messages.text({
to: '15551234567',
body: 'Hello!',
});
console.log('Message sent:', response.messages[0].id);
} catch (error: any) {
const apiError = error.response?.data?.error;
if (apiError) {
console.error(`API Error [${apiError.code}]: ${apiError.message}`);
console.error(`Trace ID: ${apiError.fbtrace_id}`);
} else if (error.request) {
// Network error -- no response received
console.error('Network error:', error.message);
} else {
console.error('Unexpected error:', error.message);
}
}

Retry with Exponential Backoff

For retryable errors, implement exponential backoff to avoid overwhelming the API:

async function withRetry<T>(
fn: () => Promise<T>,
maxRetries = 3,
initialDelay = 1000,
): Promise<T> {
let lastError: any;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error: any) {
lastError = error;
const code = error.response?.data?.error?.code;
// Do not retry non-retryable errors
if (code === 100 || code === 190 || code === 131047) {
throw error;
}
if (attempt < maxRetries) {
const delay = initialDelay * Math.pow(2, attempt);
const jitter = delay * 0.2 * (Math.random() * 2 - 1);
await new Promise(r => setTimeout(r, delay + jitter));
}
}
}
throw lastError;
}
// Usage
const response = await withRetry(
() => client.messages.text({ to: '15551234567', body: 'Hello!' }),
3, // max retries
1000, // start with 1s delay
);

Rate Limit Handling

The WhatsApp Cloud API enforces rate limits per phone number. When you hit a limit (code 130429), the response may include a Retry-After header:

try {
await client.messages.text({ to, body });
} catch (error: any) {
const code = error.response?.data?.error?.code;
if (code === 130429) {
const retryAfter = error.response?.headers?.['retry-after'];
const waitMs = retryAfter ? Number(retryAfter) * 1000 : 60000;
console.warn(`Rate limited. Retrying after ${waitMs}ms`);
await new Promise(r => setTimeout(r, waitMs));
// Retry the request
await client.messages.text({ to, body });
} else {
throw error;
}
}

Handling the 24-Hour Window

WhatsApp enforces a 24-hour customer service window. You can only send free-form messages within 24 hours of the customer’s last message. After that, you must use an approved template:

try {
await client.messages.text({ to, body: 'Follow-up message' });
} catch (error: any) {
const code = error.response?.data?.error?.code;
if (code === 131047) {
// Window expired -- fall back to a template message
await client.messages.template({
to,
template: {
name: 'follow_up',
language: { code: 'en' },
},
});
} else {
throw error;
}
}

Webhook Error Handling

When processing inbound webhooks, always wrap your handler to prevent unhandled exceptions from crashing the server:

async function handleWebhookMessage(client: WhatsApp, message: any) {
try {
// Your message handling logic
await processMessage(message);
} catch (error) {
console.error('Webhook handler error:', error);
// Optionally notify the user
try {
await client.messages.text({
to: message.from,
body: 'Sorry, something went wrong. Please try again.',
});
} catch {
// If even the error notification fails, just log it
console.error('Failed to send error notification');
}
}
}

Always return a 200 status to Meta’s webhook delivery system, even if your processing fails. Returning non-200 codes causes Meta to retry delivery, which can lead to duplicate processing.

Debugging Tips

  1. Log the fbtrace_id from every error response. Meta support can use this to investigate issues on their end.

  2. Enable SDK debug mode to see verbose request/response logs:

    const client = new WhatsApp({
    accessToken: process.env.CLOUD_API_ACCESS_TOKEN!,
    phoneNumberId: Number(process.env.WA_PHONE_NUMBER_ID),
    businessAcctId: process.env.WA_BUSINESS_ACCOUNT_ID,
    debug: true,
    });
  3. Check the Graph API Explorer at developers.facebook.com/tools/explorer to test API calls interactively and see raw error responses.

  4. Monitor error rates over time. A sudden spike in 130429 (rate limit) errors may indicate you need to implement message queuing, while persistent 190 errors signal a token expiry issue.

Further Reading

For advanced patterns including circuit breakers, structured logging, and error monitoring integration, see the Error Handling Guide.