Skip to main content

Troubleshoot number lookup and messaging-service resolution failures

Two families of failures stop a send before it enqueues:
  • LOOKUP_FAILED — the number-lookup endpoints (GET /api/v1/numbers/lookup/:phone, the trust-package dip, and bulk HLR) lost their upstream carrier-intelligence feed and could not answer.
  • Messaging-service resolution codesMESSAGING_SERVICE_NOT_FOUND (404) and MESSAGING_SERVICE_RESOLUTION_FAILED (500), raised when the sender-resolution chain cannot bind the service id the request named.
Match the code to its branch below before you retry. A lookup failure runs in your send pre-flight — either as the explicit numbers/lookup dip or indirectly when the sender-resolution chain consults lookup for country/MCC heuristics — so a repeated 502 there can block your whole launch.

1. LOOKUP_FAILED on GET /api/v1/numbers/lookup

The number-lookup service tries the primary HLR provider first and falls back to the secondary Number Lookup provider when HLR throws. When the primary provider is unreachable and the fallback is also down — or when no provider is configured at all — the service cannot answer at all and the route returns a 502:

Cause table

The lineStatus gate is the important fallback: when a lookup returns lineStatus: "unknown" you should not treat the number as inactive, because a “no signal” result must stay distinguishable from a true “inactive” — treat the number as possibly valid until a confirmed lookup returns inactive.

Retry matrix

In all transient branches the response carries meta.request_id; capture it so support can trace the exact upstream call that failed.
A bulk lookup (POST /api/v1/numbers/lookup/bulk-hlr) must do the same per-row backoff — retrying the whole batch on one row’s failure re-charges the rows that succeeded. Filter to failed[] first, then resubmit those E.164s only.

2. Messaging-service resolution — MESSAGING_SERVICE_NOT_FOUND and MESSAGING_SERVICE_RESOLUTION_FAILED

Every outbound send must resolve a sender. The resolution chain checks the messaging-service pointer first, then a sender-pool override, then the tenant fallback. Two codes cover the failure modes that stop the chain before it even reaches pool lookup. A send naming nothing at all — neither messaging_service_id, nor a sender_pool_id, nor a from E.164 / sender ID — fails earlier with 422 SENDER_REQUIRED (covered in the sender-resolution errors runbook). The two codes above are the failure modes after you do name a service.

MESSAGING_SERVICE_NOT_FOUND — pointer is stale

The id you sent does not match a service that your tenant owns. Check:
  1. List your services. GET /api/v1/messaging/messaging-services returns the current inventory; pick the live id and update the client / template / campaign that is pointing at the deleted one.
  2. Re-create if needed. When a service was deleted, any sender-pool override or campaign bound to it keeps hitting this 404 until you repoint it.
  3. Test-mode / sandbox. In sandbox modes the service list is per-environment — an id copied from a live production call will not resolve against the sandbox organisation.
If the pointer is correct but the failure persists across two or more calls, escalate with the meta.request_id — an orphaned pointer cluster usually means the row is still present but the tenant scoping failed, which is a platform-side problem.

MESSAGING_SERVICE_RESOLUTION_FAILED — transient resolution error

The service row was found, but a downstream storage error (for example a Drizzle-wrapped Postgres error) interrupted the resolution before any of the optional overrides could be resolved. The fix:
  • Retry once. This code is retryable; the wire message is deliberately static so the client never receives raw storage text, and a single retry usually succeeds when the cause was a transient database blip.
  • Probe the pools. If the failure recurs and always on the same service, run GET /api/v1/messaging/sender-pools?messaging_service=<id> to see whether a broken pool override (country_sender_pools pointing at a deleted pool) is what the chain is dying on. Fix that override on the service.
Both codes are recovered by returning a definitively-resolving service or sender-pool — a retry against the same broken pointer without the fix will just re-trip.

3. Who decides, and when to escalate

For a LOOKUP_FAILED: you check whether the failure is request-shape (lookup the wrong E.164, an empty body) or upstream (both providers down). The first branch is a client fix; the second needs Support. For a messaging-service resolution code: you decide which selector your integration should actually carry. The tenant’s default fallback chain only runs when the request named nothing. Either you repoint the messaging service, or you repoint the sender pool, or the fallback from picks up — but you cannot decide that the platform owns the pointer until you have run GET /api/v1/messaging/messaging-services and confirmed the id is not there. If the id is listed and the send still fails, escalate. When you escalate, include:
  • The exact meta.request_id of one failed call.
  • For LOOKUP_FAILED: the E.164, the branch (HLR-only or fallback) and, if present, the provider_message hint.
  • For messaging-service codes: the service id and the step in the chain the error came from (messaging_service_id pointer or sender-pool override).
Support owns the platform branch; a missing or broken service pointer is a tenant-owned control, so the run on this page is the “who tries next” division.
  • Sender resolution — the precedence chain every send walks: pool over bare from, messaging_service_id fills in only when you named neither, and the fallback order.
  • Messaging services model — the entity the messaging_service_id pointer targets.
  • Live number testing (LNT) — the other send pre-flight that can land a lookup failure: when a lookup rejects in LNT the live test never bills.
  • Number lookup — the lookup surface the LOOKUP_FAILED code protects.
  • Sender-resolution errors — the other codes the same chain can throw (SENDER_REQUIRED, SENDER_POOL_NOT_FOUND, NO_SENDER_CONFIGURED).
  • Error codes reference — the wire surface all these codes are registered on.