A2P SDK
Guides

Error Handling

Error Handling

The SDK provides a structured error hierarchy for all A2P registration failure modes.

Error Hierarchy

All errors extend A2PRegistrationError:

class A2PRegistrationError extends Error {
  step: string;          // e.g., "customer_profile", "brand_registration"
  sid?: string;          // Resource SID (if available)
  failureReason?: string; // Twilio's failure reason
  guidance?: string;     // Actionable troubleshooting advice
}

Error Types

Error ClassStepWhen Thrown
ProfileRejectedErrorcustomer_profileCustomer Profile or Starter Profile rejected
TrustProductFailedErrortrust_productTrust Product fails evaluation or review
BrandFailedErrorbrand_registrationBrand fails TCR review
BrandSuspendedErrorbrand_registrationBrand is suspended
CampaignFailedErrorcampaign_registrationCampaign fails manual vetting
OtpVerificationErrorsole_prop_otp_verificationOTP verification times out

Catching Errors

Use instanceof checks to handle specific errors:

import {
  A2PRegistrationError,
  ProfileRejectedError,
  BrandFailedError,
  CampaignFailedError,
} from '@warp-message/a2p-sdk';

try {
  const result = await flows.directStandard.step1_createCustomerProfile(client, input);
} catch (error) {
  if (error instanceof ProfileRejectedError) {
    console.error('Profile rejected:', error.guidance);
    // Error has: step, sid, failureReason, guidance
  } else if (error instanceof BrandFailedError) {
    console.error('Brand failed:', error.guidance);
  } else if (error instanceof A2PRegistrationError) {
    console.error('Registration error:', error.step, error.guidance);
  } else {
    console.error('Unexpected error:', error);
  }
}

Error Guidance

Each error includes a guidance property with actionable troubleshooting advice:

ProfileRejectedError

Guidance: "The email domain must not be disposable. The address must be a valid US or Canadian address. Check the Twilio Console for detailed rejection reasons."

Common causes:

  • Disposable email domain (e.g., temp-mail.org)
  • Invalid US/Canadian address
  • Business name doesn't match public records

BrandFailedError

Guidance: "Common causes: business name mismatch with tax records, invalid EIN, or incomplete business information. See the Twilio Console for TCR rejection details."

Common causes:

  • Business name doesn't match IRS/tax records exactly
  • Invalid or mismatched Tax ID (EIN/CBN)
  • Incomplete business information

BrandSuspendedError

Guidance: "The Brand potentially violates one or more rules. Contact Twilio Support for details."

Action: No programmatic fix. Contact Twilio Support.

CampaignFailedError

Guidance: "You can edit and resubmit the campaign (additional $15 fee may apply). Common issues: vague description, insufficient message samples, or policy violations."

Common causes:

  • Vague campaign description (< 40 chars or too generic)
  • Insufficient message samples (< 2 samples)
  • Policy violations (spam, adult content, etc.)

Action: Edit campaign and resubmit (costs $15).

OtpVerificationError

Guidance: "The customer must respond to the OTP SMS within 24 hours. You can re-trigger the OTP from the Twilio Console. The mobile number can only be used 3 times total for Sole Proprietor registrations across all vendors."

Common causes:

  • Customer didn't verify OTP within 24 hours
  • OTP SMS blocked or not delivered
  • Mobile number already used 3 times (hard limit across ALL vendors)

Error Chaining

All errors use the standard cause property for error chaining:

try {
  const result = await step1_createCustomerProfile(client, input);
} catch (error) {
  if (error instanceof A2PRegistrationError) {
    console.error('Original error:', error.cause);
  }
}

Catch-All Handler

Build a generic error handler:

async function handleRegistrationStep<T>(
  fn: () => Promise<T>
): Promise<T | null> {
  try {
    return await fn();
  } catch (error) {
    if (error instanceof A2PRegistrationError) {
      console.error(`[${error.step}] Registration failed:`, error.guidance);
      if (error.failureReason) {
        console.error('Twilio reason:', error.failureReason);
      }
      return null;
    }
    throw error; // Re-throw unexpected errors
  }
}

// Usage
const result = await handleRegistrationStep(() =>
  flows.directStandard.step1_createCustomerProfile(client, input)
);

Next Steps

On this page