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

# Errors and recovery

> Handle OAuth errors, v2 API error envelopes, validation failures, and archive conflicts.

# Errors and recovery

The v2 API returns OAuth error bodies for OAuth flows and structured error envelopes for v2 resource requests.

## Error shapes

<Tabs>
  <Tab title="OAuth" icon="key-round">
    OAuth endpoints return `error` and `error_description`.

    ```json Response theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "error": "invalid_scope",
      "error_description": "Scope not allowed for this application: products.write"
    }
    ```
  </Tab>

  <Tab title="v2 API" icon="braces">
    v2 endpoints return `success`, `code`, and `message` when a structured error body is available.

    ```json Response theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "success": false,
      "code": "insufficient_scope",
      "message": "Token must include one of: products.write"
    }
    ```
  </Tab>
</Tabs>

## Common status codes

| Status | Meaning                                                               | Recovery                                                              |
| ------ | --------------------------------------------------------------------- | --------------------------------------------------------------------- |
| `400`  | The request body, query, or OAuth grant is invalid.                   | Validate field names, required properties, and OAuth form fields.     |
| `401`  | Credentials are missing, invalid, or cannot authenticate the request. | Check Basic auth, client headers, bearer tokens, and rotated secrets. |
| `403`  | The credential is valid but lacks an accepted scope.                  | Add the required scope to the application or token request.           |
| `404`  | The requested resource does not exist for the tenant.                 | Re-read the list endpoint and use the returned resource ID.           |
| `409`  | The requested archive operation conflicts with current state.         | Read the record, resolve dependent state, then retry when safe.       |

## Recovery playbooks

<AccordionGroup>
  <Accordion title="A write request returns 403" icon="shield-alert">
    Confirm that the developer application has the write scope for that resource. Then confirm the token request included that scope if you are using bearer tokens.
  </Accordion>

  <Accordion title="A product create request returns 400" icon="square-x">
    Check that the request includes `name` and at least one entry in `variations`. Verify field names use snake\_case and enum values match the API reference.
  </Accordion>

  <Accordion title="A resource lookup returns 404" icon="search-x">
    The ID may belong to another tenant, may be archived outside the active view, or may not exist. Re-list the resource with the relevant filters before retrying.
  </Accordion>

  <Accordion title="An archive request returns 409" icon="archive-x">
    A product variation archive can conflict with current product state. Re-read the product and archive records in the order required by your workflow.
  </Accordion>
</AccordionGroup>

## Error handling example

<CodeGroup>
  ```javascript Node.js theme={"theme":{"light":"github-light","dark":"github-dark"}}
  async function invoiceEdgeJson(response) {
    const text = await response.text();
    const body = text ? JSON.parse(text) : null;

    if (!response.ok) {
      const detail = body?.message || body?.error_description || response.statusText;
      throw new Error(`Vantr ${response.status}: ${detail}`);
    }

    return body;
  }
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
  def invoice_edge_json(response):
      try:
          body = response.json()
      except ValueError:
          body = None

      if not response.ok:
          detail = None
          if isinstance(body, dict):
              detail = body.get("message") or body.get("error_description")
          raise RuntimeError(f"Vantr {response.status_code}: {detail or response.reason}")

      return body
  ```
</CodeGroup>

<Tip>
  Log the HTTP status, endpoint, and safe error body fields. Do not log client secrets, bearer tokens, or full authorization headers.
</Tip>
