# Errors

> The two failure shapes, the reason and action fields, and what an agent should do for each.

URL: https://mcp-docs.opennomos.com/docs/reference/errors

OpenNomos reports problems in two different ways, because they mean different things.

## 1. A call that was rejected

The request itself was wrong or not allowed. Over REST this is an HTTP error with a JSON body; over MCP the tool result has `isError: true` and the same JSON as text.

```json
{ "message": "This tool requires the SEO & AI visibility pack", "code": "pack_required",
  "details": { "pack": "seo", "url": "https://tools.opennomos.com/console/packs", "action": "give_up_and_tell_user" } }
```

| `code` | HTTP | Meaning and what to do |
| --- | --- | --- |
| `invalid_input` | 400 | A parameter is wrong, missing, or unknown (schemas are strict, so an unrecognised key is rejected). `message` is the first problem found. Fix and retry. |
| `invalid_request` | 400 | Default for other bad requests (for example both `days` and `deadline` given). |
| `unauthorized` | 401 | Finish OAuth or replace the key. The response carries a `WWW-Authenticate` pointer to the OAuth metadata. |
| `pack_required` | 403 | The pack is not enabled. Tell the user. |
| `soft_limit` | 429 | Monthly upstream cost limit reached. Tell the user. |
| `link_host_not_allowed` | 400 / 403 | Short link target is not https, is an IP or shortener, or would be a fourth target host. |
| `experiment_not_found`, `opportunity_not_found` | 404 | The id does not exist in this project. |
| `conflict` | 409 | Changed concurrently. Call again. |

A rate limit on writes or searches also returns an error; slow down and retry.

## 2. A source that did not answer

The call was fine, but the upstream source could not give data. This is returned as a **normal result** (HTTP 200) so that one failing source does not hide the others:

```json
{ "error": "The source is rate limiting requests.", "reason": "rate_limited", "action": "retry_later", "retry_after_seconds": 30 }
```

In [`search_signals`](https://mcp-docs.opennomos.com/docs/tools/research/search_signals.md) the same fields appear inside the failing source's entry (`status: "unavailable"`), next to successful ones.

| `reason` | Meaning | `action` |
| --- | --- | --- |
| `rate_limited` | The source is limiting requests. May carry `retry_after_seconds`. | `retry_later` |
| `timeout` | The source did not answer in time. | `retry_later`, or `try_other_tool` |
| `upstream_error` | The source returned an error. | `retry_later`, or `try_other_tool` |
| `invalid_input` | The source rejected the input. Check the parameters. | `give_up_and_tell_user` |
| `not_found` | The source has nothing at this address or id. | `give_up_and_tell_user` |
| `upstream_account` | OpenNomos's own access to the source is not working (credentials or credit on our side). Retrying will not help. | `give_up_and_tell_user` |
| `not_configured` | The source is not configured on this server. | `give_up_and_tell_user` |

| `action` | What the agent should do |
| --- | --- |
| `retry_later` | Transient. Wait (`retry_after_seconds` if given) and try the same call once more. |
| `try_other_tool` | The same job can be done through the tool named in `tool` (for example `search_signals` for a failed SERP or news call). |
| `give_up_and_tell_user` | Retrying cannot help. Say so to the user. |

<Callout type="warn">
A failure means the source gave no data. It does **not** mean there are no relevant results. Never conclude "nobody is talking about this" from a failed call.
</Callout>

## Short results

When a source returns fewer items than requested, the result is still a success and carries a one-sentence `note`, for example *Requested 10 organic results; the source returned 7. That is everything it returned (it may have no more matches, or may cap results).* The note states only what was observed.
