> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Troubleshoot number lookup and messaging-service resolution failures

> Decode `LOOKUP_FAILED` on the number-lookup endpoints and the messaging-service resolver codes `MESSAGING_SERVICE_RESOLUTION_FAILED` / `MESSAGING_SERVICE_NOT_FOUND` that block a send before it takes off.

# 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 codes** — `MESSAGING_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:

```json theme={null}
{
  "error": {
    "code": "LOOKUP_FAILED",
    "status": 502,
    "message": "Number lookup request failed"
  }
}
```

### Cause table

| Code                                                | What happened                                                                                                                         | Recover by                                                                                                              |
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `LOOKUP_FAILED` (502)                               | The primary HLR provider and the fallback Number Lookup provider are both unreachable, or the fallback timed out after backup retries | Apply a bounded backoff (below) and retry; a single 502 that clears after one retry is the normal transient path        |
| `LOOKUP_FAILED` with `details.provider_message` set | The fallback returned a non-OK status (upstream reject, quota error, or malformed request) that was too unrecoverable to retry        | Inspect the `provider_message` string — it is server-side redacted provider text safe for wire output                   |
| Recurring 502 on a specific E.164                   | A carrier-scale failure (the HLR feed for that number's MCC/MNC is unsynced)                                                          | Treat the lookup as failed-open — proceed without the enrichment fields and plan a re-dip, or gate on `lineStatus` only |

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

| Failure                   | Retry behaviour                                                                                                                                                                                                             |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 502 `LOOKUP_FAILED`       | Retry with backoff — start 1 s, double per attempt, cap at 30 s — up to 3 attempts; treat a still-502 after 3 attempts as "provider is down" and fall back to a degraded path (send without enrichment, or skip the number) |
| 503 `SERVICE_UNAVAILABLE` | Do not retry blind — this means the whole lookup stage is unconfigured; check the integration settings page first                                                                                                           |
| `lineStatus: "unknown"`   | Not a 502 — a valid lookup with no signal. Retry the lookup once after 5 s and treat a second "unknown" as the honourable "no data" answer                                                                                  |

In all transient branches the response carries `meta.request_id`; capture it so support
can trace the exact upstream call that failed.

<Note>
  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.
</Note>

***

## 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.

| Code                                  | HTTP | What fired                                                                                                              |
| ------------------------------------- | ---- | ----------------------------------------------------------------------------------------------------------------------- |
| `MESSAGING_SERVICE_NOT_FOUND`         | 404  | The body named `messaging_service_id`, but no row with that id belongs to your organisation                             |
| `MESSAGING_SERVICE_RESOLUTION_FAILED` | 500  | The service row exists, but an unexpected storage error (for example a transient DB hiccup) occurred while resolving it |

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](/troubleshooting/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`](/concepts/messaging-services-model) 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.

***

## Related references

* [Sender resolution](/concepts/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](/concepts/messaging-services-model) — the entity the
  `messaging_service_id` pointer targets.
* [Live number testing (LNT)](/guides/telq-live-number-testing) — the other send pre-flight
  that can land a lookup failure: when a lookup rejects in LNT the live test never bills.
* [Number lookup](/numbers/lookup) — the lookup surface the `LOOKUP_FAILED` code protects.
* [Sender-resolution errors](/troubleshooting/sender-resolution-errors) — the other codes
  the same chain can throw (`SENDER_REQUIRED`, `SENDER_POOL_NOT_FOUND`,
  `NO_SENDER_CONFIGURED`).
* [Error codes reference](/reference/error-codes) — the wire surface all these codes are
  registered on.
