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

# Errors

> Handle API errors gracefully

## Error Format

All error responses follow a consistent structure:

```json theme={"system"}
{
  "data": null,
  "errors": [
    {
      "message": "Missing required field: name",
      "layer": "product"
    }
  ]
}
```

### Reading the Errors Array

The `errors` array is ordered from root cause to top-level caller:

* `errors[0]` -- the **root cause** of the failure (most useful for debugging)
* `errors[n]` -- the top-level caller that surfaced the error

In most cases, you only need to inspect `errors[0].message` for the actionable error description.

***

## HTTP Status Codes

| Code | Meaning         | Description                                    |
| ---- | --------------- | ---------------------------------------------- |
| 200  | Success         | Request processed successfully                 |
| 400  | Bad Request     | Invalid parameters or malformed request body   |
| 401  | Unauthorized    | Missing or invalid authentication credentials  |
| 403  | Forbidden       | Valid credentials but insufficient permissions |
| 404  | Not Found       | Requested resource does not exist              |
| 409  | Conflict        | Idempotent request is already being processed  |
| 429  | Rate Limited    | Too many requests in a short period            |
| 500  | Server Error    | Internal server error                          |
| 501  | Not Implemented | Requested feature is not yet available         |
| 502  | Bad Gateway     | Upstream service error                         |

***

## Error `layer` Field

Each error includes a `layer` string indicating which part of the system produced the error. Use the `layer` value alongside `message` to identify the root cause when debugging.

***

## Common Error Scenarios

### Authentication Errors (401)

```json theme={"system"}
{
  "data": null,
  "errors": [
    {
      "message": "Token has expired",
      "layer": "user"
    }
  ]
}
```

**Solutions:**

* Verify your Merchant ID and private key are correct
* Ensure the SDK is initialized with valid credentials
* Check that your private key matches the registered public key

### Validation Errors (400)

```json theme={"system"}
{
  "data": null,
  "errors": [
    {
      "message": "Store name must be between 1 and 100 characters",
      "layer": "store"
    }
  ]
}
```

**Solutions:**

* Check required fields are present
* Verify field value formats and constraints
* Ensure amounts are display format strings (e.g., "29.00")

### Permission Errors (403)

```json theme={"system"}
{
  "data": null,
  "errors": [
    {
      "message": "Only store owner can delete store",
      "layer": "store"
    }
  ]
}
```

**Solutions:**

* Verify the API Key has the required permissions
* Ensure the user belongs to the correct store

### Idempotency Conflicts (409)

```json theme={"system"}
{
  "data": null,
  "errors": [
    {
      "message": "Request with this idempotency key is already being processed",
      "layer": "gateway"
    }
  ]
}
```

**Solutions:**

* Wait for the original request to complete
* Use a different `X-Idempotency-Key` for a new request

***

## Handling Errors in Code

### TypeScript SDK

```typescript theme={"system"}
import { WaffoPancake, WaffoPancakeError } from "@waffo/pancake-ts";

const client = new WaffoPancake({
  merchantId: process.env.WAFFO_MERCHANT_ID!,
  privateKey: process.env.WAFFO_PRIVATE_KEY!,
});

try {
  const { store } = await client.stores.create({ name: "My Store" });
} catch (err) {
  if (err instanceof WaffoPancakeError) {
    const rootCause = err.errors[0];
    console.error(`Error [${rootCause.layer}]: ${rootCause.message}`);
    // err.status contains the HTTP status code
  }
}
```

***

## Retry Strategy

Retry these status codes with exponential backoff:

* **429** -- Rate limited (respect `Retry-After` header if present)
* **500** -- Internal server error
* **502** -- Bad gateway

```javascript theme={"system"}
async function retryRequest(fn, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      const status = error.status;
      if (![429, 500, 502].includes(status) || attempt === maxRetries - 1) {
        throw error;
      }
      const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}
```

<Note>
  Do not retry `400`, `401`, `403`, or `404` errors. These indicate issues that must be fixed in the request itself.
</Note>

***

## Idempotency for Safe Retries

Use the `X-Idempotency-Key` header to safely retry write operations without creating duplicates:

```bash theme={"system"}
curl -X POST https://api.waffo.ai/v1/actions/checkout/create-session \
  -H "Content-Type: application/json" \
  -H "X-Store-Slug: your-store-slug" \
  -H "X-Environment: test" \
  -H "X-Idempotency-Key: order-abc-123" \
  -d '{"productId": "...", "productType": "onetime", "currency": "USD"}'
```

* Idempotency keys are cached for **24 hours**
* Sending the same key returns the cached response
* If the original request is still processing, returns `409 Conflict`
