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

# Handle StoreRouter API errors in your integration

> Understand the StoreRouter error model, SDK exception types, and recovery strategies for auth failures, missing resources, and validation errors.

The StoreRouter API uses standard HTTP status codes for errors. The Python and TypeScript SDKs translate every non-2xx response into a typed exception, so you can catch specific error conditions without inspecting raw HTTP status codes. Understanding which errors are transient and which require a code or configuration change helps you build integrations that fail fast on bugs and recover gracefully from network issues.

## Error categories

StoreRouter API errors fall into two categories:

1. **HTTP errors (4xx/5xx)** — The server returned a non-2xx response. The SDK raises a typed exception with a `status_code`, `detail`, and the raw `response` attached.
2. **Network errors** — The request never reached the server (DNS failure, timeout, connection reset). The SDK raises `OctogenConnectionError`.

<Note>
  If you are using the Developer MCP server instead of the REST API, tool-level errors are returned as HTTP 200 responses with an `error` field in the payload rather than as HTTP error codes. This guide covers the REST API only.
</Note>

## HTTP error reference

<AccordionGroup>
  <Accordion title="400 — Bad request">
    **SDK exception:** `OctogenAPIError` with `status_code` (Python) / `statusCode` (TypeScript) equal to `400`

    The request was syntactically valid JSON but could not be accepted for the requested operation.

    **Recovery:** Inspect the response details, correct the request, and retry.
  </Accordion>

  <Accordion title="401 — Authentication error">
    **SDK exception:** `OctogenAuthenticationError`

    The API key is missing, malformed, or has been revoked. Common causes:

    * `OCTO_API_KEY` environment variable is not set
    * The key string was truncated or contains extra whitespace
    * The key was deactivated in StoreRouter Platform

    **Recovery:** Rotate or replace the API key. Do not retry the request with the same key.
  </Accordion>

  <Accordion title="403 — Forbidden">
    **SDK exception:** `OctogenForbiddenError`

    The API key is valid, but your organization is not allowed to perform this operation. Common causes:

    * Your organization does not have Developer access
    * The API key belongs to the wrong organization
    * The key's org type does not have permission for the endpoint

    **Recovery:** Check your organization's access in StoreRouter Platform.
  </Accordion>

  <Accordion title="402 — Insufficient prepaid credit">
    **SDK exception:** `OctogenAPIError` with `status_code` (Python) / `statusCode` (TypeScript) equal to `402`

    Your prepaid organization does not have enough available credit for the request. This response is not charged.

    **Recovery:** Add credit or wait for your next monthly free-credit renewal. See [API pricing and billing](/docs/guides/pricing-and-billing).
  </Accordion>

  <Accordion title="404 — Not found">
    **SDK exception:** `OctogenNotFoundError`

    The requested catalog or product is not visible to your API key. Common `detail` values:

    * `"catalog_not_found"` — The catalog key you passed does not identify an active crawled catalog available to this organization.
    * `"product_not_found"` — The Product Lookup sources selected by `resolutionMode` did not return a useful product. For `index_only`, no granted active catalog matched the URL.

    **Recovery:** Retry with a known active crawled catalog key, or omit the optional `catalog` field for policy-wide search. For Product Lookup, confirm the URL is a product page and choose the appropriate [resolution mode](/docs/guides/product-lookup#choose-a-resolution-mode).
  </Accordion>

  <Accordion title="422 — Validation error">
    **SDK exception:** `OctogenValidationError`

    The request body failed schema validation. Product Lookup can also return `detail: "invalid_product_url"` for a malformed or unsupported URL, or `detail: "unsafe_url"` when the target fails outbound safety checks.

    For field validation, the response body contains a `detail` array where each entry identifies the invalid field:

    ```json theme={null}
    {
      "detail": [
        {
          "loc": ["body", "limit"],
          "msg": "Input should be less than or equal to 100",
          "type": "less_than_equal",
          "input": 500,
          "ctx": {"le": 100}
        }
      ]
    }
    ```

    Each entry has:

    * `loc` — path to the invalid field (e.g. `["body", "limit"]`)
    * `msg` — human-readable error message
    * `type` — machine-readable error type

    **Recovery:** Fix the request based on the detail value or field-level messages. Do not retry an unsafe URL unchanged.
  </Accordion>

  <Accordion title="429 — Rate limited">
    **SDK exception:** `OctogenAPIError` with `status_code` (Python) / `statusCode` (TypeScript) equal to `429`

    Your request exceeded a service-wide budget. The shared API budget returns `detail: "rate_limit_exceeded"`; Product Lookup can return `detail: "product_lookup_rate_limited"` when its on-demand capacity is exhausted. Both responses include a `Retry-After` header with the number of seconds to wait.

    **Recovery:** Unlike the other `4xx` errors, `429` is transient. Wait the `Retry-After` interval, then retry with exponential backoff. See [Rate Limits](/docs/guides/rate-limits) for the headers, the MCP equivalent, and bulk-job guidance.
  </Accordion>

  <Accordion title="502 — Upstream failure">
    **SDK exception:** `OctogenAPIError` with `status_code` (Python) / `statusCode` (TypeScript) equal to `502`

    Product Lookup returns `detail: "product_lookup_upstream_failed"` when the merchant page fails upstream.

    **Recovery:** Retry with exponential backoff. If the error persists, verify the merchant URL in a browser before contacting StoreRouter support.
  </Accordion>

  <Accordion title="503 — Service unavailable">
    **SDK exception:** `OctogenAPIError` with `status_code` (Python) / `statusCode` (TypeScript) equal to `503`

    A downstream StoreRouter service was temporarily unavailable. For Product Lookup, `detail: "product_lookup_fallback_unavailable"` means on-demand resolution is unavailable; use `index_only` if an indexed result is sufficient.

    **Recovery:** Retry with exponential backoff. If the error persists, contact StoreRouter support with the timestamp and request body summary.
  </Accordion>

  <Accordion title="504 — Lookup timed out">
    **SDK exception:** `OctogenAPIError` with `status_code` (Python) / `statusCode` (TypeScript) equal to `504`

    Product Lookup returns `detail: "product_lookup_timed_out"` when its bounded synchronous deadline expires.

    **Recovery:** Retry with exponential backoff. Repeated timeouts can indicate that the merchant page is slow or unavailable.
  </Accordion>
</AccordionGroup>

## SDK exception hierarchy

All SDK exceptions inherit from `OctogenError`. The full hierarchy:

```
OctogenError
├── MissingAPIKeyError          # OCTO_API_KEY not set and no api_key argument passed
├── OctogenConnectionError      # Network-level failure (timeout, DNS, connection reset)
└── OctogenAPIError             # Non-2xx HTTP response
    ├── OctogenAuthenticationError   # 401
    ├── OctogenForbiddenError        # 403
    ├── OctogenNotFoundError         # 404
    └── OctogenValidationError       # 422
```

`OctogenAPIError` and its subclasses expose:

* `.status_code` (Python) / `.statusCode` (TypeScript) — the HTTP status code
* `.detail` — the parsed error body from the `detail` field, or the raw response text
* `.response` — the underlying HTTP response object

## Handling errors in code

<CodeGroup>
  ```python python theme={null}
  import asyncio
  from octogen_ai_sdk import (
      OctogenClient,
      OctogenAuthenticationError,
      OctogenConnectionError,
      OctogenForbiddenError,
      OctogenNotFoundError,
      OctogenValidationError,
      OctogenAPIError,
  )

  async def main() -> None:
      async with OctogenClient() as client:
          try:
              result = await client.lookup_product(
                  "https://warrenlotas.com/products/black-hoodie"
              )
              print(result.product.title)

          except OctogenAuthenticationError:
              # 401 — rotate or replace the API key
              print("Invalid API key. Check OCTO_API_KEY and rotate if needed.")

          except OctogenForbiddenError as exc:
              # 403 — org does not have access
              print(f"Access denied: {exc.detail}")

          except OctogenNotFoundError as exc:
              # 404 — catalog or product not found
              print(f"Not found: {exc.detail}")

          except OctogenValidationError as exc:
              # 422 — fix the request fields
              print(f"Validation error: {exc.detail}")

          except OctogenConnectionError as exc:
              # Network failure — safe to retry with backoff
              print(f"Connection error: {exc}")

          except OctogenAPIError as exc:
              # Catch-all for unexpected 5xx or other API errors
              print(f"API error {exc.status_code}: {exc.detail}")
              raise

  asyncio.run(main())
  ```

  ```typescript typescript theme={null}
  import {
    OctogenClient,
    OctogenAuthenticationError,
    OctogenConnectionError,
    OctogenForbiddenError,
    OctogenNotFoundError,
    OctogenValidationError,
    OctogenAPIError,
  } from "@octogen-ai/sdk";

  const client = new OctogenClient();

  try {
    const result = await client.lookupProduct(
      "https://warrenlotas.com/products/black-hoodie"
    );
    console.log(result.product.title);
  } catch (error) {
    if (error instanceof OctogenAuthenticationError) {
      // 401 — rotate or replace the API key
      console.error("Invalid API key. Check OCTO_API_KEY and rotate if needed.");
    } else if (error instanceof OctogenForbiddenError) {
      // 403 — org does not have access
      console.error("Access denied:", error.detail);
    } else if (error instanceof OctogenNotFoundError) {
      // 404 — catalog or product not found
      console.error("Not found:", error.detail);
    } else if (error instanceof OctogenValidationError) {
      // 422 — fix the request fields
      console.error("Validation error:", error.detail);
    } else if (error instanceof OctogenConnectionError) {
      // Network failure — safe to retry with backoff
      console.error("Connection error:", error.message);
    } else if (error instanceof OctogenAPIError) {
      // Catch-all for unexpected 5xx or other API errors
      console.error(`API error ${String(error.statusCode)}:`, error.detail);
      throw error;
    } else {
      throw error;
    }
  }
  ```
</CodeGroup>

## Retry guidance

<Warning>
  Do not automatically retry most `4xx` errors. A `400`, `401`, `403`, `404`, or `422` response means the request itself is invalid or unauthorized — retrying it will produce the same error. Fix the underlying cause (body, key, permissions, or request target) before trying again. The exception is `429` (rate limited): retry it, but only after waiting the `Retry-After` interval. See [Rate Limits](/docs/guides/rate-limits).
</Warning>

| Error type | Retry? | Action |
| - | - | - |
| `OctogenAPIError` with 400 | No | Fix the rejected targets or request body |
| `OctogenAuthenticationError` (401) | No | Replace the API key |
| `OctogenForbiddenError` (403) | No | Check org catalog grants |
| `OctogenNotFoundError` (404) | No | Verify catalog key or product URL |
| `OctogenValidationError` (422) | No | Fix the request body |
| `OctogenAPIError` with 429 | Yes, after delay | Honor `Retry-After`, then retry with backoff |
| `OctogenConnectionError` | Yes | Retry with exponential backoff |
| `OctogenAPIError` with 502 | Yes | Retry with exponential backoff |
| `OctogenAPIError` with 503 | Yes | Retry with exponential backoff |
| `OctogenAPIError` with 504 | Yes | Retry with exponential backoff |
| `OctogenAPIError` with 5xx | Yes | Retry with exponential backoff |

For transient errors and `5xx` responses, use an exponential backoff strategy with jitter. Start with a 1-second delay, double on each retry, and cap at 30 seconds. After three to five failed attempts, surface the error to the caller rather than retrying indefinitely.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.