Profiles·Public

twilio

semver>=3.0.0 <7.0.0postconditions21functions11last verified2026-06-24coverage score92%

Postconditions: what we check

  • create · messages-create-no-try-catch
    error
    Whenmessages.create() called without try-catch or .catch() handler
    ThrowsRestException with error.code, error.status, and error.message
    Required 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 unavailablevisibilityvisible
    Sources[1]
  • create · messages-create-generic-catch
    warning
    Whenmessages.create() in try-catch but doesn't check RestException
    Required 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 unavailablevisibilityvisible
    Sources[2]
  • create · messages-create-rate-limit-not-handled
    warning
    WhenBulk SMS operations without rate limit handling
    Required handlingSHOULD check for error.code === 14107 and implement exponential backoff retry logic. Consider using retry-after information if available.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[1]
  • create · messages-create-opted-out-not-handled
    warning
    Whenmessages.create() does not handle error code 21610 (recipient opted out)
    ThrowsRestException with error.code 21610
    Required 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 performancevisibilityvisible
    Sources[3]
  • create · messages-create-geo-permission-not-handled
    warning
    Whenmessages.create() does not handle error code 21408 (geo-permission denied)
    ThrowsRestException with error.code 21408
    Required 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 performancevisibilityvisible
    Sources[4]
  • create · calls-create-no-try-catch
    error
    Whencalls.create() called without try-catch or .catch() handler
    ThrowsRestException with error.code, error.status, and error.message
    Required 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 unavailablevisibilityvisible
    Sources[1]
  • create · verifications-create-no-try-catch
    error
    Whenverifications.create() called without try-catch
    ThrowsRestException with error.code, error.status, and error.message
    Required handlingMUST wrap verification create call in try-catch block. Handle specific error codes for better user experience.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[5]
  • twilio · hardcoded-credentials
    error
    WhenTwilio client initialized with hardcoded credentials
    Required 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 unavailablevisibilityvisible
    Sources[6]
  • twilio · missing-auth-error-early-detection
    warning
    WhenInitial API call doesn't check for authentication errors
    Required 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 unavailablevisibilityvisible
    Sources[1]
  • validateRequest · webhook-no-signature-validation
    error
    WhenWebhook endpoint doesn't validate request signature
    Required handlingMUST validate webhook signatures using twilio.validateRequest() or twilio.validateExpressRequest(). Reject requests with invalid signatures (return 403).
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[7]
  • create · verification-check-no-try-catch
    error
    WhenverificationCheck.create() called without try-catch or .catch() handler
    ThrowsRestException with error.code, error.status, and error.message
    Required 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
    Sources[8][9]
  • create · verification-check-expired-not-handled
    error
    WhenverificationCheck.create() does not handle 404 for expired verifications
    ThrowsRestException with status 404
    Required 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 performancevisibilityvisible
    Sources[8]
  • create · verification-check-max-attempts-not-handled
    warning
    WhenverificationCheck.create() does not handle error code 60202 (max check attempts)
    ThrowsRestException with error.code 60202
    Required 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
    Sources[9][8]
  • fetch · lookups-fetch-no-try-catch
    error
    Whenlookups.v2.phoneNumbers().fetch() called without try-catch
    ThrowsRestException with error.code, error.status, and error.message
    Required 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
    Sources[10][11]
  • fetch · lookups-invalid-number-result-not-checked
    warning
    Whenlookups.v2.phoneNumbers().fetch() result valid field not checked
    Required 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 performancevisibilitysilent
    Sources[10]
  • update · calls-update-no-try-catch
    error
    Whencalls(sid).update() called without try-catch or .catch() handler
    ThrowsRestException 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
    Sources[12][13]
  • update · calls-update-stale-call-not-handled
    warning
    Whencalls(sid).update() does not handle error 21220 (call already ended)
    ThrowsRestException 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 performancevisibilityvisible
    Sources[13]
  • fetch · messages-fetch-no-try-catch
    error
    Whenmessages(sid).fetch() called without try-catch or .catch() handler
    ThrowsRestException 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 unavailablevisibilityvisible
    Sources[14]
  • fetch · messages-fetch-undelivered-not-checked
    warning
    Whenmessages(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 performancevisibilitysilent
    Sources[14]
  • create · recordings-create-no-try-catch
    error
    Whencalls(sid).recordings.create() called without try-catch
    ThrowsRestException 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
    Sources[15][13]
  • create · verify-services-create-no-try-catch
    error
    Whenverify.v2.services.create() called without try-catch
    ThrowsRestException 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[16][17]

Sources

Every postcondition cites at least one of these. Grouped by source type; numbered to match the footnotes above.

Official documentation
Source code

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

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_SID
    • TWILIO_AUTH_TOKEN
  • Source: https://github.com/twilio/twilio-node README

Webhook Security

  • Risk: Spoofed webhook requests
  • Mitigation: Use twilio.validateRequest() or twilio.validateExpressRequest()
  • Pattern: Verify x-twilio-signature header
  • 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:

  1. Credential exposure (hardcoded tokens)
  2. Webhook spoofing (missing signature validation)
  3. Rate limiting abuse

Note: These are implementation issues, not package vulnerabilities.


Related Resources

Twilio Changelog

GitHub Issues


Nark profile Rationale

Why These Functions?

  1. messages.create() - Most common Twilio operation, high failure rate due to validation
  2. calls.create() - Voice operations, critical for telephony apps
  3. verify.services.verifications.create() - 2FA is security-critical
  4. twilio() - Client initialization, credential management critical
  5. validateRequest() - Webhook security prevents spoofing attacks

Why These Postconditions?

  1. missing-error-handling - Twilio docs emphasize 400-level errors are "normal"
  2. hardcoded-credentials - Security best practice, prevent credential leaks
  3. missing-rest-exception-check - Access to error.code enables specific handling
  4. missing-rate-limit-handling - Free tier has limits, bulk operations need handling
  5. missing-auth-error-handling - Fail fast on configuration issues
  6. 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 patterns
  • missing-error-handling.ts - Demonstrates violations
  • instance-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