twilio
semver
>=3.0.0 <7.0.0postconditions21functions11last verified2026-06-24coverage score92%Postconditions: what we check
- create · messages-create-no-try-catcherrorWhenmessages.create() called without try-catch or .catch() handlerThrows
RestException with error.code, error.status, and error.messageRequired handlingMUST wrap await client.messages.create() in try-catch block. Catch block should check error instanceof RestException and handle specific error codes (14107 for rate limiting, 20003 for invalid credentials, 21211 for invalid phone numbers) appropriately.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - create · messages-create-generic-catchwarningWhenmessages.create() in try-catch but doesn't check RestExceptionRequired handlingSHOULD check error type using instanceof RestException and inspect error.code. Handle rate limiting (14107) with retry logic, authentication errors (20003/20005) by validating credentials, and validation errors (21211/21212) with user feedback.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[2]
- create · messages-create-rate-limit-not-handledwarningWhenBulk SMS operations without rate limit handlingRequired handlingSHOULD check for error.code === 14107 and implement exponential backoff retry logic. Consider using retry-after information if available.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1]
- create · messages-create-opted-out-not-handledwarningWhenmessages.create() does not handle error code 21610 (recipient opted out)Throws
RestException with error.code 21610Required handlingSHOULD check error.code === 21610 in the catch block and either (a) mark the recipient's profile as sms_opted_out so future messages are not attempted, (b) fall back to an alternative channel (email or push), or (c) surface the opt-out state to the operator UI. DO NOT retry — Twilio will continue to reject until the user opts back in via START.costmediumin prodimmediate exceptionusers seedegraded performancevisibilityvisibleSources[3] - create · messages-create-geo-permission-not-handledwarningWhenmessages.create() does not handle error code 21408 (geo-permission denied)Throws
RestException with error.code 21408Required handlingSHOULD check error.code === 21408 in the catch block and either (a) surface to ops telemetry so the country can be enabled in Geo Permissions, (b) reject the user-facing signup with a clear "SMS not supported in your region — please use email verification" message, or (c) fall back to an alternative verification channel. DO NOT retry — the error is configuration-level, not transient.costmediumin prodimmediate exceptionusers seedegraded performancevisibilityvisibleSources[4] - create · calls-create-no-try-catcherrorWhencalls.create() called without try-catch or .catch() handlerThrows
RestException with error.code, error.status, and error.messageRequired handlingMUST wrap await client.calls.create() in try-catch block. Catch block should check error instanceof RestException for detailed error information.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - create · verifications-create-no-try-catcherrorWhenverifications.create() called without try-catchThrows
RestException with error.code, error.status, and error.messageRequired handlingMUST wrap verification create call in try-catch block. Handle specific error codes for better user experience.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[5] - twilio · hardcoded-credentialserrorWhenTwilio client initialized with hardcoded credentialsRequired handlingMUST use environment variables for credentials. Use process.env.TWILIO_ACCOUNT_SID and process.env.TWILIO_AUTH_TOKEN. Never commit credentials to version control.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[6]
- twilio · missing-auth-error-early-detectionwarningWhenInitial API call doesn't check for authentication errorsRequired handlingSHOULD validate credentials with test API call during initialization. Check for error codes 20003 and 20005 to fail fast with clear error messages.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1]
- validateRequest · webhook-no-signature-validationerrorWhenWebhook endpoint doesn't validate request signatureRequired handlingMUST validate webhook signatures using twilio.validateRequest() or twilio.validateExpressRequest(). Reject requests with invalid signatures (return 403).costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[7]
- create · verification-check-no-try-catcherrorWhenverificationCheck.create() called without try-catch or .catch() handlerThrows
RestException with error.code, error.status, and error.messageRequired handlingMUST wrap await verificationCheck.create() in try-catch. Handle error.code 60202 (max attempts — tell user to request a new code), 404 status (code expired — prompt re-send), and error.code 20003 (auth failure — alert ops).costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - create · verification-check-expired-not-handlederrorWhenverificationCheck.create() does not handle 404 for expired verificationsThrows
RestException with status 404Required handlingSHOULD check error.status === 404 in the catch block and direct the user to request a new verification code via verifications.create().costmediumin prodimmediate exceptionusers seedegraded performancevisibilityvisibleSources[8] - create · verification-check-max-attempts-not-handledwarningWhenverificationCheck.create() does not handle error code 60202 (max check attempts)Throws
RestException with error.code 60202Required handlingSHOULD check error.code === 60202 and show a user-friendly message explaining that the code has expired and they must request a new verification code. Automatically trigger verifications.create() to send a fresh code.costmediumin prodimmediate exceptionusers seedegraded performancevisibilityvisible - fetch · lookups-fetch-no-try-catcherrorWhenlookups.v2.phoneNumbers().fetch() called without try-catchThrows
RestException with error.code, error.status, and error.messageRequired handlingMUST wrap await phoneNumbers(number).fetch() in try-catch. For user-submitted numbers, handle 21421 (invalid format) with a user-facing validation message. Handle 20003 (auth failure) with ops alerting.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - fetch · lookups-invalid-number-result-not-checkedwarningWhenlookups.v2.phoneNumbers().fetch() result valid field not checkedRequired handlingSHOULD check phoneNumber.valid === true after fetch(). If false, inspect phoneNumber.validationErrors array and return appropriate user feedback before attempting to send SMS or make calls.costlowin prodsilent failureusers seedegraded performancevisibilitysilentSources[10]
- update · calls-update-no-try-catcherrorWhencalls(sid).update() called without try-catch or .catch() handlerThrows
RestException with error.status and error.code. Error 21220 (HTTP 400) — call is no longer in-progress (already completed, failed, or cancelled); this is the most common runtime error. Error 20003 (HTTP 401) — authentication failure. Error 20429 (HTTP 429) — rate limit exceeded. NetworkError — connection timeout or DNS failure.Required handlingMUST wrap await client.calls(sid).update() in try-catch. Handle error.code === 21220 gracefully — it means the call already ended, which is usually not a fatal condition. Handle error.code === 20003 with ops alerting.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - update · calls-update-stale-call-not-handledwarningWhencalls(sid).update() does not handle error 21220 (call already ended)Throws
RestException with error.code 21220 (HTTP 400)Required handlingSHOULD check error.code === 21220 in the catch block and treat it as a no-op (the call is already ended, which was the goal of cancellation) rather than propagating as an unrecoverable error.costlowin prodimmediate exceptionusers seedegraded performancevisibilityvisibleSources[13] - fetch · messages-fetch-no-try-catcherrorWhenmessages(sid).fetch() called without try-catch or .catch() handlerThrows
RestException with error.status and error.code. HTTP 404 (status 404) — message SID not found or belongs to different account. Error 20003 (HTTP 401) — authentication failure. Error 20429 (HTTP 429) — rate limit exceeded on status polling. NetworkError — connection timeout.Required handlingMUST wrap await client.messages(sid).fetch() in try-catch. Handle error.status === 404 to detect invalid/foreign message SIDs rather than crashing the status-check loop.costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[14] - fetch · messages-fetch-undelivered-not-checkedwarningWhenmessages(sid).fetch() result status not checked for 'undelivered' or 'failed'Required handlingSHOULD check message.status after fetch(). If status is 'undelivered' or 'failed', inspect message.errorCode and message.errorMessage for the root cause and implement retry logic or user notification.costmediumin prodsilent failureusers seedegraded performancevisibilitysilentSources[14]
- create · recordings-create-no-try-catcherrorWhencalls(sid).recordings.create() called without try-catchThrows
RestException with error.status and error.code. Error 21220 (HTTP 400) — call is not in-progress (already ended, ringing, or queued); cannot start recording on an inactive call. Error 20003 (HTTP 401) — authentication failure. Error 20429 (HTTP 429) — rate limit exceeded. NetworkError — connection failure.Required handlingMUST wrap await calls(callSid).recordings.create() in try-catch. Handle error.code === 21220 gracefully — log the missed recording but do not propagate as a fatal error when the call already ended.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - create · verify-services-create-no-try-catcherrorWhenverify.v2.services.create() called without try-catchThrows
RestException with error.status and error.code. HTTP 400 — invalid parameters (e.g., friendlyName too long, invalid codeLength). Error 20003 (HTTP 401) — authentication failure, invalid Account SID or Auth Token. Error 20429 (HTTP 429) — rate limit exceeded (too many service creation attempts). NetworkError — connection failure.Required handlingMUST wrap await verify.v2.services.create() in try-catch. On error, log with full error.code context and fail the tenant provisioning flow with a clear user-facing error rather than leaving the tenant in a half-initialized state.costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
Sources
Every postcondition cites at least one of these. Grouped by source type; numbered to match the footnotes above.
Official documentation
- [1]twilio.com/docs/api/errorsErrors
- [3]twilio.com/docs/api/errors21610
- [4]twilio.com/docs/api/errors21408
- [5]twilio.com/docs/verify/apiVerification
- [6]twilio.com/docs/usage/secure-credentialsSecure Credentials
- [7]twilio.com/docs/usage/webhooksWebhooks Security
- [8]twilio.com/docs/verify/apiVerification Check
- [9]twilio.com/docs/errors/6020260202
- [10]twilio.com/docs/lookup/apiApi
- [11]twilio.com/docs/errors/2142121421
- [12]twilio.com/docs/voice/apiCall Resource
- [13]twilio.com/docs/errors/2122021220
- [14]twilio.com/docs/sms/apiMessage Resource
- [15]twilio.com/docs/voice/apiRecording Resource
- [16]twilio.com/docs/verify/apiService
- [17]twilio.com/docs/errors/2000320003
Source code
- [2]github.com/twilio/twilio-nodetwilio/twilio-node
Research notes
Curator notes from SOURCES.md captured when the profile was written so you can verify the reasoning, not just the rules.
Sources: twilio
This document tracks the research sources used to create the Nark profile for the twilio package.
Official Documentation
Error Handling & Diagnostics
- URL: https://www.twilio.com/docs/conversations/error-handling-diagnostics
- Accessed: 2026-02-25
- Key Findings:
- Connection state changes must be monitored
- All async operations return result objects requiring success verification
- "Denied" state indicates Access Token problems
- 401 errors indicate permissions issues
- Enable DEBUG-level logging for diagnostics
Error and Warning Dictionary
- URL: https://www.twilio.com/docs/api/errors
- Accessed: 2026-02-25
- Key Findings:
- 10001-19999: Account and authentication errors
- 11200-11243: HTTP and connection errors
- 12000-14111: Validation errors
- 14107: SMS send rate limit exceeded
- 20003: Invalid credentials
- 20005: Account suspended
- 21xxx: Messaging-specific validation errors
- Complete JSON error reference available for download
GitHub Repository
- URL: https://github.com/twilio/twilio-node
- Accessed: 2026-02-25
- Key Findings:
- Promise-based error handling with
.catch() - Async/await with try-catch support
- RestException class provides: code, message, status, moreInfo
- Environment variable pattern for credentials
- CommonJS and ES6 import patterns supported
- Promise-based error handling with
NPM Package
- URL: https://www.npmjs.com/package/twilio
- Accessed: 2026-02-25
- Key Findings:
- 400/500 level HTTP responses throw errors
- Both promise and callback-based error handling
- RestException can be imported for type checking
Error Code Categories
Authentication Errors
- 20003: Authenticate (invalid AccountSid or AuthToken)
- 20005: Account suspended
- Pattern: Check these errors early to fail fast
Rate Limiting Errors
- 14107: SMS send rate limit exceeded
- 20429: Too many requests
- Pattern: Implement retry logic with exponential backoff
Validation Errors
- 21211: Invalid 'To' phone number
- 21212: Invalid 'From' phone number
- 21408: Permission to send SMS not enabled
- 21610: Unsubscribed recipient
- Pattern: Validate input before API calls
HTTP Errors
- 401 Unauthorized: Authentication failure
- 403 Forbidden: Permission denied
- 404 Not Found: Resource doesn't exist
- 503 Service Unavailable: Twilio service issue
- Pattern: Retry with backoff for 503, fail for 401/403
Security Considerations
Credential Management
- Risk: Hardcoded credentials in source code
- Mitigation: Always use environment variables
- Environment Variables:
TWILIO_ACCOUNT_SIDTWILIO_AUTH_TOKEN
- Source: https://github.com/twilio/twilio-node README
Webhook Security
- Risk: Spoofed webhook requests
- Mitigation: Use
twilio.validateRequest()ortwilio.validateExpressRequest() - Pattern: Verify
x-twilio-signatureheader - Source: Twilio Security Best Practices
Common API Operations
Sending SMS
const message = await client.messages.create({
body: 'Hello from Twilio',
to: '+12345678901',
from: '+10987654321'
});
Making Calls
const call = await client.calls.create({
url: 'http://demo.twilio.com/docs/voice.xml',
to: '+12345678901',
from: '+10987654321'
});
Verification (2FA)
const verification = await client.verify
.services(serviceSid)
.verifications
.create({ to: '+12345678901', channel: 'sms' });
Error Handling Patterns
Basic Try-Catch
try {
const message = await client.messages.create({...});
} catch (error) {
console.error('Failed to send message:', error);
throw error;
}
RestException Check
import { RestException } from 'twilio';
try {
const message = await client.messages.create({...});
} catch (error) {
if (error instanceof RestException) {
console.error(`Twilio error ${error.code}: ${error.message}`);
// Handle specific error codes
if (error.code === 14107) {
// Rate limited - retry later
} else if (error.code === 21211) {
// Invalid phone number
}
}
throw error;
}
Promise-based
client.messages.create({...})
.then((message) => console.log(message.sid))
.catch((error) => {
if (error instanceof RestException) {
console.error(error.code, error.message);
}
});
CVE Analysis
Search Date: 2026-02-25 Search Query: "twilio npm CVE"
Result: No critical CVEs found for the twilio npm package.
Main Security Concerns:
- Credential exposure (hardcoded tokens)
- Webhook spoofing (missing signature validation)
- Rate limiting abuse
Note: These are implementation issues, not package vulnerabilities.
Related Resources
Twilio Changelog
- URL: https://www.twilio.com/en-us/changelog/twilio-functions--node-js-v22-becomes-the-default-runtime-on-jun
- Note: Node.js v22 becomes default runtime June 11, 2026
GitHub Issues
- URL: https://github.com/twilio/twilio-node/issues
- Notable Issue: #949 - Unhandled rejection in SDK when catching exceptions
- Resolution: Use try-catch for async operations
Nark profile Rationale
Why These Functions?
- messages.create() - Most common Twilio operation, high failure rate due to validation
- calls.create() - Voice operations, critical for telephony apps
- verify.services.verifications.create() - 2FA is security-critical
- twilio() - Client initialization, credential management critical
- validateRequest() - Webhook security prevents spoofing attacks
Why These Postconditions?
- missing-error-handling - Twilio docs emphasize 400-level errors are "normal"
- hardcoded-credentials - Security best practice, prevent credential leaks
- missing-rest-exception-check - Access to error.code enables specific handling
- missing-rate-limit-handling - Free tier has limits, bulk operations need handling
- missing-auth-error-handling - Fail fast on configuration issues
- missing-webhook-signature-verification - Security critical for webhook endpoints
Severity Justifications
ERROR-level (5):
- Missing try-catch on API calls - Can cause unhandled promise rejections
- Hardcoded credentials - Security vulnerability
- Missing webhook verification - Security vulnerability
WARNING-level (3):
- Missing RestException check - Reduces error handling quality
- Missing rate limit handling - Can cause service degradation
- Missing auth error handling - Reduces debuggability
Contract Testing
Test Fixtures
proper-error-handling.ts- Demonstrates correct patternsmissing-error-handling.ts- Demonstrates violationsinstance-usage.ts- Tests client instance detection
Expected Results
- Proper handling: 0 violations
- Missing handling: 5+ violations
- Instance usage: Correct detection of client.messages.create() patterns
Maintenance Notes
Last Updated: 2026-02-25 Reviewed By: Claude Sonnet 4.5 Contract Version: 1.0.0
Next Review: When major Twilio SDK version is released or error handling patterns change.
Need a different package?
Request a profile