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

# Common errors

Errors come back as a JSON-RPC **`bad_request`** (HTTP 400) with `{ code: "bad_request", detail, request_id }`. The SDK throws with `detail` — a human-readable message string, **not** a typed error object. Match on the substring shown below.

User-authentication failures additionally carry a **machine code at the front** of `detail` (e.g. `eth_authenticator_limit: …`), so the SDK can branch with a single `startsWith`.

## Tenant operations — register, maps, dispatch

| You'll see in `detail`                                                            | Cause                                                                                                                                                                                                          | Fix                                                                                                                                                                                                                                                                                                                         |
| --------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `version <x> is not higher than current version <y>`                              | Re-registering a contract at a version that isn't greater than the deployed one                                                                                                                                | Bump the `version` passed to `contracts.register`                                                                                                                                                                                                                                                                           |
| `map already exists`                                                              | Re-running `maps.create` against an already-provisioned tenant                                                                                                                                                 | Idempotent — safe to ignore on re-runs                                                                                                                                                                                                                                                                                      |
| `map not found`                                                                   | A map tail in `kv_store::get` / `put` doesn't match what `maps.create` created                                                                                                                                 | Match the tails exactly between [Create tenant KV maps](/developers/adk/tips/create-kv-maps) and your Rust                                                                                                                                                                                                                  |
| `canonical map name invalid: <reason>`                                            | `tail` is empty, contains `..`, or starts with `z:`                                                                                                                                                            | Pass only the local tail (e.g. `"secrets"`) — the SDK prefixes `z:<tid>:`                                                                                                                                                                                                                                                   |
| `quota exceeded: <dim>` (e.g. `quota exceeded: max_contracts`)                    | Hit a per-tenant quota                                                                                                                                                                                         | Ask the cluster operator to raise the quota                                                                                                                                                                                                                                                                                 |
| `access denied: <caller> cannot <op> map "<map>"`                                 | The contract isn't on the map's `readers` / `writers` ACL                                                                                                                                                      | `tenant.maps.update` to add the contract id to `readers` / `writers`                                                                                                                                                                                                                                                        |
| `tenant is suspended`                                                             | The operator suspended your tenant                                                                                                                                                                             | Ask the operator to resume                                                                                                                                                                                                                                                                                                  |
| `host/http.egress_denied: host '<host>' is not in the authorised_hosts allowlist` | The contract called a host the caller's `agent_auth` grant doesn't authorize                                                                                                                                   | Add the host to the user's grant (see [Invoke your contract](/developers/adk/get-started/walkthrough/invoke-contract))                                                                                                                                                                                                      |
| `InsufficientCreditError` on a metered function call from an **agent** identity   | Metered calls are charged against the **calling identity's own** T3N credit balance — an agent DID's balance is separate from its tenant's, and starts at zero even when the tenant has plenty of test tokens. | There's currently no documented self-serve path for an agent (as opposed to a tenant) to acquire its own credit balance — token transfers are an admin-only action. Contact [devrel@terminal3.io](mailto:devrel@terminal3.io) or the [developer Telegram](https://t.me/terminal3developer) to get an agent identity funded. |

<Note>
  Contract-authored errors are whatever **your** contract returns. The flight
  example, for instance, surfaces `duffel_api_key not found in z:<tid>:secrets —
    populate it via the tenant SDK` when the `secrets` map wasn't seeded (see
  [Seed API key into secrets map](/developers/adk/tips/seed-api-key)) — that's
  the contract's own message, not a platform error.
</Note>

## Generic / opaque failures (HTTP 500, no clear cause)

A `bad_request` gives you a substring to match on. A bare **`HTTP 500`** with no further detail is a different situation — it usually isn't your contract logic. Before assuming it's a bug in your code, check these in order:

1. **Grab the `request_id`.** Most 500 responses still include one — save it before you start changing code. If you end up asking for help, it's the single most useful thing you can hand over.
2. **Re-check egress and ACLs first.** A missing outbound-host grant or a missing map ACL entry can surface as a 500 instead of the more specific error you'd expect — re-read the [egress](/developers/adk/tips/outbound-http-auth-by-user) and [map ACL](/developers/adk/tips/create-kv-maps) requirements before looking further.
3. **Retry once, deliberately.** A single unhealthy node can return a 500 for requests that are otherwise correct. If an identical request succeeds on retry, it was very likely transient — no code change needed.
4. **If it's consistent and reproducible**, it's more likely a platform-side issue than your integration. Report it in the [developer Telegram](https://t.me/terminal3developer) with the `request_id`, rather than spending hours guessing at workarounds.

## Common integration gotchas

These aren't errors with a fixed message — they're patterns that produce confusing behavior downstream, reported independently by multiple teams.

| Gotcha                                                | What happens                                                                                                                                                                                                                                                                                                                                                   | Fix                                                                                                                          |
| ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| Hex-encoding a DID before building a map path         | `format!("z:{}:secrets", hex::encode(&tenant_did()))` double-encodes — `tenant_did()` already returns the string form, so wrapping it in `hex::encode` again produces a value that doesn't match any map you actually created, and lookups silently fail to find it.                                                                                           | Use `tenant_did()`'s return value directly in the map path — don't re-encode it.                                             |
| Omitting `baseUrl` when constructing a `TenantClient` | `TenantClient`'s config accepts `baseUrl` as an optional field, but calls can still fail at request time without it, even after calling `setEnvironment()`. This is specific to `TenantClient` — `T3nClient` (used for tenant, agent, and user sessions in the walkthrough) doesn't take a `baseUrl` at all; it's always resolved from the active environment. | When constructing a `TenantClient`, always pass `baseUrl: getNodeUrl()` explicitly rather than relying on it being inferred. |

## Authentication & wallet linking

These come from the user/session contract during sign-in and `addAuthMethod`, with the code at the **front** of `detail`:

| Code (prefix of `detail`) | When                                                                        |
| ------------------------- | --------------------------------------------------------------------------- |
| `eth_authenticator_limit` | Hit the cap on wallets linked to one DID (e.g. trying to add an 11th)       |
| `eth_auth_map_conflict`   | The wallet is already linked to a different DID — resolve via account merge |
| `email_not_verified`      | A profile upsert ran before the email OTP was verified                      |
| `user_not_found`          | The DID has no profile yet                                                  |
| `legacy_field`            | A pre-2.0.0 dispatch field was sent (e.g. `otp_code` on `user-upsert`)      |
