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

# Errors and warnings

> Every error code the Titan MCP server returns, whether it is worth retrying, and the warning codes that tell an agent what to do next.

The MCP server distinguishes two kinds of signal. **Errors** mean the call did not do what you asked. **Warnings** mean it did, with something worth knowing.

An agent should stop on errors and read on through warnings.

## Error shape

Failures return an MCP error result whose content is a structured JSON body:

```json theme={null}
{
  "code": "limit_exceeded",
  "message": "max_pages exceeds hard cap of 100",
  "retryable": false,
  "request_id": "req_8f3a1c9e2b7d4056",
  "run_id": "3c7d1a92-6f48-4b25-9e03-7d1a4c8b2f65",
  "details": {}
}
```

| Field        | Meaning                                                           |
| ------------ | ----------------------------------------------------------------- |
| `code`       | Stable machine-readable code. Branch on this, never on `message`. |
| `message`    | Human-readable explanation                                        |
| `retryable`  | Whether retrying could plausibly succeed                          |
| `request_id` | Correlates this call with Titan's logs                            |
| `run_id`     | Present when a run had already been created                       |
| `details`    | Additional context, such as the specific URL rejected             |

## Error codes

### Before the run starts

These fail preflight. No execution is created and no credits are consumed.

| Code                   | Meaning                               | Fix                                                      |
| ---------------------- | ------------------------------------- | -------------------------------------------------------- |
| `invalid_request`      | A parameter is missing or malformed   | Correct the parameters                                   |
| `unauthorized`         | Missing or invalid API key            | Check the key and how your client sends it               |
| `forbidden`            | Key lacks the scope this tool needs   | Create a key with the right scope                        |
| `limit_exceeded`       | A value exceeds a hard cap            | Lower it—see [Limits and safety](/docs/mcp/limits-and-safety) |
| `unsafe_url`           | URL failed safety validation          | Use a public HTTP or HTTPS URL                           |
| `template_not_found`   | Slug is not MCP-allowlisted           | Use a slug from `titan_list_templates`                   |
| `insufficient_credits` | Balance is below the request estimate | Top up, or request less work                             |

### During execution

These happen after a run exists, so the run is inspectable in the dashboard and API.

| Code                    | Retryable | Meaning                                        |
| ----------------------- | :-------: | ---------------------------------------------- |
| `backend_unavailable`   |    Yes    | Titan could not be reached; retry with backoff |
| `timeout`               |    Yes    | The operation exceeded its window              |
| `provider_rate_limited` |    Yes    | The search provider throttled the request      |
| `provider_failed`       | Sometimes | The provider failed to return usable results   |

<Tip>
  Branch on the `retryable` field rather than maintaining your own list of codes. The server sets it per error, and it stays correct as codes evolve.
</Tip>

## Warning codes

Warnings appear in the `warnings` array on successful calls. The array is always present, often empty.

| Code                   | Meaning                                                | What to do                                       |
| ---------------------- | ------------------------------------------------------ | ------------------------------------------------ |
| `next_step`            | The run outlived the wait window                       | Call `titan_get_run` with the `run_id`           |
| `operator_unsupported` | A search field was dropped for this provider           | Switch provider if the operator matters          |
| `provider_no_results`  | Search completed with zero organic results             | Broaden the query                                |
| `cross_origin_dropped` | Crawl records outside the seed host were removed       | Expected; seed a different host if you need them |
| `idempotency_active`   | A run for this key is already in flight                | Poll the returned `run_id`                       |
| `idempotency_replay`   | A finished run for this key was replayed               | Use the returned result                          |
| `steps_unavailable`    | Step data could not be loaded, so results were omitted | Retry `titan_get_run`                            |

<Warning>
  `steps_unavailable` is not an empty result. It means results could not be loaded right now. Treat it as retryable—concluding "nothing was found" from it is wrong.
</Warning>

## Handling errors in an agent

```typescript theme={null}
const result = await client.callTool({ name: "titan_fetch", arguments: args });

if (result.isError) {
  const error = JSON.parse(result.content[0].text);

  if (error.retryable) {
    await sleep(backoffMs);
    return retry();
  }

  throw new Error(`${error.code}: ${error.message}`);
}

const output = JSON.parse(result.content[0].text);

if (output.warnings?.some((w) => w.code === "next_step")) {
  return pollRun(output.run_id);
}

return output;
```

Two branches carry most of the weight: retry when `retryable` is true, and poll when `next_step` appears.

## Transport-level errors

Connection failures return HTTP status codes rather than tool error bodies:

| Status | Code                            | Cause                              |
| ------ | ------------------------------- | ---------------------------------- |
| 401    | `missing_authentication`        | No `Authorization` header          |
| 401    | `invalid_api_key`               | Key unknown, revoked, or malformed |
| 401    | `query_string_api_key_rejected` | Key sent as a URL parameter        |
| 403    | `insufficient_scopes`           | Key carries no `mcp:*` scope       |

These mean the connection itself is misconfigured. See [Connect your client](/docs/mcp/connect-your-client).

## Debugging with request\_id

Every response carries a `request_id`, and every run carries a `run_id`. Together they locate a call precisely:

```bash theme={null}
# What the run actually did
curl -sS "$TITAN_API_URL/api/v1/executions/$RUN_ID" \
  -H "Authorization: Bearer $TITAN_TOKEN"

# Per-step detail, including failures
curl -sS "$TITAN_API_URL/api/v1/executions/$RUN_ID/steps" \
  -H "Authorization: Bearer $TITAN_TOKEN"
```

Include the `request_id` when reporting an issue—it is the fastest way to find the call in Titan's logs.

## Next steps

* [Idempotency and retries](/docs/mcp/idempotency-and-retries) — retry safely
* [Limits and safety](/docs/mcp/limits-and-safety) — what triggers `limit_exceeded` and `unsafe_url`
* [HTTP errors and exceptions](/docs/api-reference/errors) — the REST API equivalents
