Twilio Verify SMS OTP Codes Not Arriving: Diagnosing Carrier Filtering and Error 30003/30005/30006
Reach 50,000+ security professionals
Sponsor a newsletter slot and put your brand in front of CISOs, engineers, and IT decision-makers.
An MFA rollout built on Twilio Verify tends to work fine for most users and quietly fail for a subset of them, and the failure usually shows up as a support ticket that says only 'I never got my code,' not as an application error. From the application's point of view, the Verify API often returned a normal 201 response and a pending status. Nothing in that response tells you whether the code is still in transit, was blocked by the destination carrier, or was never deliverable to that number in the first place.
The cause is one of three distinct things, and Twilio's own error codes describe them precisely once you know where to look: 30003 (unreachable destination handset), 30005 (unknown destination handset), and 30006 (landline or unreachable carrier). They are easy to treat as interchangeable 'delivery failed' signals, but they point to different root causes, only some of which an application-side fix can resolve, and conflating them leads teams to either under-react to a permanent carrier block or over-react to a transient, self-resolving delay.
This guide covers what actually causes each error code, how to pull the error out of Twilio's logs since the Verify API does not surface it directly in its own response, how to tell whether failures are clustering by carrier or region, the application-side and account-side fixes for each cause, how to confirm a fix actually improved delivery rather than just changed which error code appears, what happens to users when no fallback channel exists, and when the fix requires opening a case with Twilio support instead of changing application code.
Causes: what 30003, 30005, and 30006 actually mean
Error 30003, unreachable destination handset, means the destination phone is temporarily unavailable on the carrier network: powered off, out of coverage, or otherwise unable to receive SMS at that moment. This is usually transient. A code sent to the same number a few minutes later, once the device is back online, will frequently succeed. It is rarely a sign of a systemic problem unless it recurs against the same number repeatedly.
Error 30005, unknown destination handset, means the carrier reports the number itself as unknown or inactive, most commonly a number that has been deactivated, ported, or reassigned since it was collected. It is not always that simple, though: carrier-side content and sender-reputation filtering, most visibly on T-Mobile, can also produce a 30005 against a number that is active and working, when the carrier's spam-filtering system blocks the specific message as unwanted application-to-person (A2P) traffic rather than because the number is actually invalid. This is the case that gets misdiagnosed most often, because the error code looks identical whether the number is genuinely dead or the carrier simply filtered the message.
Error 30006, landline or unreachable carrier, most commonly means the destination number is a landline or a number type incapable of receiving SMS at all, which is a permanent condition, not a transient one. It can also indicate the destination carrier's network is unreachable for reasons unrelated to the specific handset.
Underneath all three, the structural cause that makes filtering worse than it needs to be is an unregistered 10DLC campaign. US carriers, coordinating through The Campaign Registry (TCR), throttle and increasingly block A2P SMS traffic from senders that have not registered a campaign describing their use case and expected volume. A Verify Service sending one-time codes through an unregistered or under-registered sender is far more likely to have its traffic filtered by carrier spam systems than one sending through a properly registered, OTP-specific campaign, because unregistered traffic looks identical to unsolicited bulk messaging from the carrier's side.
Diagnostics: pulling the error code the Verify API does not hand you directly
The Verify API's check step tells you whether the code the user entered was correct. It does not, by itself, tell you why a code never arrived, and a pending verification status is not evidence that delivery is still in progress; it can just as easily mean the underlying message already failed. The actual delivery error lives on the Message resource that Verify creates behind the scenes, and it has to be looked up separately.
In the Twilio Console, go to Monitor > Logs > Messaging, or query the Messages resource via the API, and look for the message associated with the failed verification's timestamp and destination number. The error_code field on that message resource is where 30003, 30005, and 30006 actually appear. If your integration only calls the Verify API and never inspects the underlying message status, wire up Twilio's status callback webhook on the Messaging Service or Verify Service so a failed delivery produces an event your application can log with its error code, rather than silently sitting in Console logs nobody is watching.
Once error codes are visible, isolate whether failures cluster by carrier or region rather than looking uniformly random. Cross-reference the destination numbers behind repeated 30005 or 30006 failures against a carrier lookup (Twilio's Lookup API returns the carrier name for a number). If failures concentrate heavily on one or two carriers, most often the largest US carriers with the most aggressive spam-filtering systems, that pattern points to carrier-side A2P filtering rather than an account-wide or application-side problem. If failures are spread evenly across carriers with no concentration, or cluster by area code and number age instead, that points more toward invalid or reassigned numbers in the underlying user data.
Briefings like this, every morning before 9am.
Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.
Fixes: registration, sender configuration, and a fallback channel
10DLC campaign registration through The Campaign Registry is the fix that addresses filtering at its source rather than working around it. Register a campaign under the 2FA/OTP use case specifically, since TCR treats one-time-passcode traffic as a distinct, lower-friction category from general marketing or conversational A2P traffic, with carriers applying comparatively lighter filtering to properly registered OTP campaigns. An unregistered or generically registered sender competing in the same filtering queue as bulk marketing traffic is the single most common reason a legitimate OTP program sees elevated 30005 rates.
Messaging Service configuration matters alongside registration. Send OTP traffic through a Messaging Service with a dedicated sender pool rather than a single long code shared with other traffic types, and confirm the service's sender pool includes numbers or short codes appropriate for the destination country's throughput expectations. A properly configured Messaging Service also gives Twilio's own routing logic more room to select a sender with better current carrier standing, rather than every message going out through one number that may have accumulated a poor reputation with a specific carrier.
Fallback to a non-SMS channel is the fix for users who keep failing despite registration and sender configuration being correct, and it should exist before those users start filing tickets, not after. Twilio Verify supports a voice channel (channel=call) that delivers the code via an automated phone call instead of SMS, which sidesteps SMS-specific carrier filtering entirely. For users on carriers or number types (like VoIP or reassigned numbers) that persistently fail SMS delivery, route them to voice OTP automatically after a defined number of SMS failures, or offer a TOTP authenticator app (Google Authenticator, Authy, or similar) as a permanent alternative that removes SMS delivery from the authentication path altogether.
Validation: confirm delivery improved, not just that the error code changed
The mistake to avoid here is treating a changed error code as proof of a fix. If a number that previously failed with 30005 now fails with 30006, or the failure rate for a specific carrier simply moves to a different error code rather than dropping, the underlying delivery problem has not actually been solved, only relabeled. Validation has to measure the delivery success rate itself, broken out by carrier, over a period after the fix, not the presence or absence of any specific error code.
Pull message logs for the two weeks before and after a 10DLC registration or sender configuration change, grouped by carrier, and compare the delivered-to-attempted ratio for each carrier in both periods. A real fix shows a measurable improvement in that ratio for the carriers that were previously concentrated with failures, not just a shift in which error code those failures carry. Because carrier filtering behavior can also change independently of anything you did (carriers periodically adjust their own filtering rules), track this on an ongoing basis rather than as a one-time check immediately after the change, since a fix that looked effective in the first week can degrade later if a carrier tightens its filtering again.
Also validate the fallback channel itself, not just the primary SMS path. Confirm that users routed to voice OTP or an authenticator app after repeated SMS failures actually complete authentication through that path at an acceptable rate; a fallback that exists in code but that users abandon before completing is not actually solving the lockout problem.
Failure cases: users locked out of MFA with no visible error
The worst version of this failure is a user who is completely locked out of an account because their SMS OTP never arrives and no fallback channel was ever configured. From the user's side, this looks identical to a broken login, not a carrier filtering issue, and it typically is not visible to the support team either unless someone specifically checks message logs, because the application layer usually only sees a timeout waiting for a code that was never going to arrive.
This produces a second failure case that compounds the first: a support burden made of repeated 'I never got my code' tickets that get treated as one-off user error or app bugs, because nobody has connected the pattern to carrier-side filtering concentrated on specific number ranges or carriers. Each individual ticket looks like an isolated incident; only aggregating them against the carrier-clustering diagnostic above reveals that they are the same systemic issue recurring across different users on the same carrier.
Both failure cases share a root cause: an MFA design that assumes SMS delivery is reliable enough to be the only channel, with no fallback and no monitoring of the underlying message delivery rate. An MFA rollout carrying real account-security weight should treat SMS delivery failure as an expected, monitored condition with a defined fallback, not an edge case that only gets attention once support tickets accumulate.
Escalation criteria: application-side fix versus a Twilio support case
This is entirely an application-side fix, with no need to involve Twilio support, when: the Verify integration never checks the underlying Message resource's error_code and is flying blind on delivery status, no 10DLC campaign is registered at all, the Messaging Service has no fallback channel configured, or failures are spread evenly across carriers and correlate with old or reassigned numbers in your own user data rather than clustering on specific carriers. All of these are things a development team can fix without any account-level intervention from Twilio.
Open a Twilio support case when: 10DLC registration is already complete and verified as approved, sender configuration follows Twilio's own recommendations, and failures still concentrate heavily and persistently on a specific carrier at a rate that looks disproportionate to that carrier's normal filtering behavior. Twilio support can confirm your campaign's actual vetting and throughput status with the carrier, check for account-level or number-level reputation issues that are not visible from the Console alone, and in some cases escalate a dispute directly with the carrier when a properly registered, compliant campaign is still being filtered at an unusual rate.
Also escalate to Twilio support, rather than treating it as a pure application bug, whenever the error pattern itself is ambiguous from the message logs, for example when a Messaging Service's status callback is not returning an error code at all for messages the Console shows as undelivered. That gap between what the API surfaces and what the Console shows is a known limitation of the Verify and Messaging APIs rather than something an application-side change can work around, and it needs Twilio to confirm what data is actually available for that specific failure.
The bottom line
SMS OTP delivery failures in Twilio Verify trace back to one of three distinct conditions behind error codes 30003, 30005, and 30006: a temporarily unreachable handset, an unknown or carrier-filtered destination, or a landline or permanently unreachable carrier, and only some of these resolve with an application-side fix. The Verify API itself does not surface the failing error code directly; it has to be pulled from the underlying Message resource's logs or a status callback webhook, and the diagnostic step that actually separates carrier filtering from bad data is checking whether failures cluster by carrier rather than assuming any single cause. 10DLC campaign registration and a properly configured Messaging Service address filtering at its source, but a voice OTP or authenticator app fallback is what actually prevents a user from being locked out while any of this is being fixed. Treat a changed error code after a fix as inconclusive until the delivery success rate itself, broken out by carrier, shows measurable improvement.
Frequently asked questions
What does Twilio error 30003 mean for a Verify SMS code?
Error 30003 means the destination handset was unreachable on the carrier network at the time of delivery, typically because the device was powered off or out of coverage. This is usually transient, and a code resent a few minutes later to the same number often succeeds without any configuration change needed.
Does Twilio error 30005 always mean the phone number is invalid?
Not always. Error 30005, unknown destination handset, can mean the number is genuinely deactivated or reassigned, but it can also appear when a carrier, most visibly T-Mobile, applies spam or A2P filtering to a message sent to an active, working number. The way to tell them apart is checking whether 30005 failures concentrate on a specific carrier rather than appearing evenly across your user base.
How do I see the Twilio error code behind a failed Verify SMS if the Verify API doesn't return one?
Look up the underlying Message resource in Twilio Console under Monitor > Logs > Messaging, or query the Messages API for the destination number and timestamp, since the error_code field lives there rather than on the Verify check response. Configuring a status callback webhook on the Messaging Service lets your application capture that error code automatically instead of checking the Console manually.
Will 10DLC registration fix Twilio Verify SMS delivery problems?
It fixes delivery problems caused by carrier-side A2P filtering of unregistered or under-registered traffic, which is a common cause of elevated 30005 rates. Register the campaign under the 2FA/OTP use case specifically, since carriers apply lighter filtering to properly registered one-time-passcode traffic than to unregistered or generic A2P traffic. It will not fix 30003 or 30006 failures caused by an actually unreachable handset or a landline number.
What should happen when a user repeatedly fails to receive their Twilio Verify SMS code?
After a defined number of consecutive SMS delivery failures to the same number, the application should automatically offer a fallback channel, either Twilio Verify's voice call channel (channel=call) or a TOTP authenticator app enrollment, rather than leaving the user stuck retrying SMS indefinitely. Without a configured fallback, repeated SMS failures on a filtered or unreachable number lock the user out of MFA entirely with no path to recovery.
When should I open a Twilio support case for SMS OTP delivery failures instead of changing my application?
Open a support case once 10DLC registration is confirmed complete and Messaging Service sender configuration follows Twilio's recommendations, but failures still concentrate heavily on a specific carrier at a rate that looks disproportionate. Twilio support can check account and number-level reputation data not visible in the Console and, in some cases, escalate a filtering dispute directly with the carrier for a properly registered, compliant campaign.
Sources & references
- Twilio Support: Troubleshooting undelivered Authy and Verify SMS text messages
- Twilio Help Center: SMS Carrier Filtering in the United States and Canada
- Twilio Support: Error 30005 Message Delivery - Unknown destination handset
- Twilio Support: Error 30006 Message Delivery - Landline or unreachable carrier
- DEV Community: How to handle Twilio Verify 30005 and 30006 errors
Free resources
Critical CVE Reference Card 2025–2026
25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.
Ransomware Incident Response Playbook
Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.
Daily briefings + Google Top Stories in one step.
Enter your email and we'll subscribe you to the daily briefing and add Decryption Digest to your Google Search feed at the same time.

Founder & Cybersecurity Evangelist, Decryption Digest
Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.
