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

# Errors

> Handle validation, account, revision, upstream and partial-result failures from v3 tools.

A v3 tool can fail while the MCP transport succeeds. Always inspect the tool result: a failure has
`isError: true` and a structured body. Read `structuredContent.code` and the fields beside it, never
the rendered text.

## The error shape

```json theme={null}
{
  "isError": true,
  "structuredContent": {
    "code": "REVISION_CONFLICT",
    "message": "Campaign cmp_01JB… changed since revision 4.",
    "recovery": "fix_input",
    "field": "expectedRevision",
    "suggestion": "Read the campaign again and retry with its current revision."
  }
}
```

| Field        | Meaning                                                                         |
| ------------ | ------------------------------------------------------------------------------- |
| `code`       | Stable, machine-readable reason. Branch on this.                                |
| `message`    | A sentence for people. It can change; do not parse it.                          |
| `recovery`   | What to do next: `retry`, `fix_input`, `ask_user`, `contact_support` or `none`. |
| `field`      | The input field at fault, when there is one.                                    |
| `suggestion` | A concrete next step, when the server has one.                                  |

## Two results that are not errors

<ResponseField name="needs_input" type="result">
  `{ "status": "needs_input", "question": "…", "missingFields": ["budget.currency"] }`. A required
  fact is missing. Ask the person the question, then call again with the answer. Never invent a
  value to get past it.
</ResponseField>

<ResponseField name="pending_confirmation" type="result">
  `{ "status": "pending_confirmation", "confirmationUid": "cnf_…", "summary": "…", "expiresAt": "…",
      "writeExecuted": false }`. The write is paused for a person's approval. Show the summary; after
  they approve, call the same tool again with `confirm: true` and the `confirmationUid` (for a
  campaign launch, `confirmLaunch: true`). Confirmations expire after 15 minutes.
</ResponseField>

## Common codes

| Code                                    | What happened                                                                                 | What to do                                                              |
| --------------------------------------- | --------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| `VALIDATION_ERROR`, `BAD_REQUEST`       | The input did not match the schema.                                                           | Fix `field` and call again. Check the [Tool Catalog](/v3/tool-catalog). |
| `CURRENCY_MISMATCH`                     | The budget currency differs from the advertiser's.                                            | Use the advertiser's primary currency.                                  |
| `UNAUTHORIZED`                          | No valid credential.                                                                          | Re-run OAuth or check the API key.                                      |
| `FORBIDDEN`, `ACCESS_DENIED`            | The account or role cannot do this.                                                           | Call `get_status`; switch accounts or ask an admin.                     |
| `TOS_ACCEPTANCE_REQUIRED`               | The organization has not accepted the current terms.                                          | Ask an admin to accept them in Plan & billing.                          |
| `NOT_FOUND`                             | No such object in the active account.                                                         | Check the id and the active account.                                    |
| `REVISION_CONFLICT`                     | Someone changed the object since you read it.                                                 | Read it again and retry with the new `expectedRevision`.                |
| `CONFLICT`                              | The same idempotency key was used with a different body, or the object is in the wrong state. | Use a new key for a new attempt; re-read state.                         |
| `BUYER_SETUP_REQUIRED`                  | Buyer setup is incomplete for this seller or advertiser.                                      | Follow the next action from `get_status`.                               |
| `INSUFFICIENT_MEDIA_BUDGET`             | Allocations exceed the campaign budget.                                                       | Lower allocations or raise the budget.                                  |
| `CAPABILITY_NOT_SUPPORTED`              | The seller or account cannot do this.                                                         | Choose another seller or approach.                                      |
| `RATE_LIMITED`                          | Too many requests.                                                                            | Wait for `Retry-After`, then retry.                                     |
| `SERVICE_UNAVAILABLE`, `INTERNAL_ERROR` | A temporary platform problem.                                                                 | Retry with the same idempotency key and backoff.                        |

<Note>
  `NOT_FOUND` deliberately hides whether an object exists in some other account. Do not read it as
  evidence about accounts you cannot see.
</Note>

## Partial results

Some tools talk to many sellers at once, so success is not all-or-nothing.

* **Proposal requests** report each seller separately: `quoted`, `products` or `failed`, with a
  summary such as "Asked 9 sellers · 6 responded · 2 pending · 1 failed". Keep the good answers; one
  failed seller does not spoil the execution.
* **Campaign launch** returns `mediaBuysExecuted` and one entry in `errors` per failed media buy, each
  with `recovery` (`transient`, `correctable` or `terminal`) and `retrySafe`. Launching again
  resubmits only the failed draft buys.

## Retrying safely

1. If a call timed out or the connection dropped, **read first**: the write may have happened.
2. Retry a write with the **same** `idempotencyKey` only if it is the same logical attempt. Change
   the key when you change the request.
3. After a `REVISION_CONFLICT`, re-read, re-decide, then write with the new revision. Do not blindly
   bump the number.
