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

# Error Codes & Troubleshooting

> Complete reference of error codes, HTTP statuses, and response formats for the Houdini Partner API v2.

## Response Format

All v2 error responses follow this shape:

```json theme={null}
{
  "code": "QUOTE_OVER_LIMIT",
  "message": "Unable to perform exchange, quote over 100000 USD",
  "requestId": "94eb4a6f-12ca-4f22-9d0c-7ffd65ace1c7"
}
```

| Field | Type | Description |
| - | - | - |
| `code` | string | Machine-readable error code (stable enum value). **Match on this field.** |
| `message` | string | Human-readable description. May contain dynamic values — use regex if needed. |
| `requestId` | string | Unique request identifier for support/debugging. |

> [!TIP]
>
> Always match on the `code` string, not the HTTP status or message text. The `code` values are a stable enum and won't change without a breaking version bump.

### Special Response Shapes

**Validation errors** include a `fields` object with per-field details:

```json theme={null}
{
  "code": "VALIDATION_ERROR",
  "message": "Validation Failed",
  "requestId": "...",
  "fields": {
    "amount": {
      "message": "Amount must be positive",
      "value": -5
    }
  }
}
```

**Rate limit errors** include retry metadata:

```json theme={null}
{
  "code": "RATE_LIMIT_EXCEEDED",
  "message": "FREE tier: 20 quote requests per minute. Try again in 45 seconds.",
  "requestId": "...",
  "retryAfter": 45,
  "meta": {
    "retryAfter": 45,
    "limit": 20,
    "windowType": "minute",
    "operationType": "quote",
    "tier": "free"
  }
}
```

***

## Error Codes by Endpoint

### `GET /v2/quotes`

| HTTP | Code | Message | Notes |
| - | - | - | - |
| 422 | `VALIDATION_ERROR` | Validation Failed | Missing/invalid params. Includes `fields` object. |
| 422 | `QUOTE_OVER_LIMIT` | Dynamic max value. | |
| 422 | `AMOUNT_TOO_LOW` | Dynamic min value. | |
| 422 | `UNSUPPORTED_FROM_TOKEN` | Unsupported \`from\` Token | Token ID not found or disabled. |
| 422 | `UNSUPPORTED_TO_TOKEN` | Unsupported \`to\` Token | Token ID not found or disabled. |
| 422 | `TO_AND_FROM_CANNOT_BE_THE_SAME` | \`to\` and \`from\` cannot be both the same token | |
| 422 | `UNSUPPORTED_ANON_TOKEN` | Unsupported \`anonymousToken\` | Invalid intermediary token for private quotes. |
| 422 | `SWAP_AMOUNT_IS_OUT_OF_BOUNDS` | Provider-level min/max exceeded. | |
| 422 | `XMR_SWAP_AMOUNT_IS_OUT_OF_BOUNDS` | Private swap intermediary bounds exceeded. | |
| 422 | `DEX_AND_FIXED_RATE_NOT_SUPPORTED` | DEX providers and fixed rate cannot be used together | `fixed=true` with a DEX quote type. Fixed rate is available for standard and private (CEX) routing only. |
| 422 | `EXACT_OUT_REQUIRES_FIXED_RATE` | Receive amount mode (amountType=receive) requires fixed=true | Reverse quotes have no floating variant. Add `fixed=true`. |
| 422 | `EXACT_OUT_NOT_SUPPORTED_FOR_DEX` | Receive amount mode is not supported for DEX-only quotes | `amountType=receive` on a DEX-only quote request. |
| 403 | `FIXED_RATE_NOT_ALLOWED` | Fixed-rate quotes are not enabled for this partner. | Account-level toggle. Contact your Houdini representative to enable it. |
| 403 | `FLOATING_RATE_NOT_ALLOWED` | Floating-rate quotes are not enabled for this partner. | Account-level toggle. Contact your Houdini representative to enable it. |
| 429 | `RATE_LIMIT_EXCEEDED` | Includes `retryAfter` and `meta`. | |
| 503 | `PRICE_QUOTES_NOT_RETRIEVED` | Could not retrieve price quotes. Try to use a different amount or a different pair. If using Private mode, try Semi-Private | All providers failed to return quotes. |
| 503 | `ANONYMOUS_DISABLED` | Anonymous exchanges are temporarily disabled. Please contact support for more details! | Feature flag is off. |
| 500 | `INTERNAL_SERVER_ERROR` | Internal Server Error | Catch-all for unexpected errors. |

### `POST /v2/exchanges`

| HTTP | Code | Message | Notes |
| - | - | - | - |
| 422 | `VALIDATION_ERROR` | Validation Failed | Missing/invalid body fields. Also: expired quote (message includes max age). |
| 422 | `INVALID_QUOTE` | Invalid Quote | Quote not found, expired, or wrong type. |
| 422 | `INVALID_PATH` | Path is invalid | The swap route/path from the quote is invalid or unavailable. |
| 422 | `ADDRESS_TO_INVALID` | addressTo is invalid | Generic address validation failure. |
| 422 | `ADDRESS_TO_INVALID_FOR_CHAIN` | Chain-specific address validation failure. | |
| 422 | `ADDRESS_FROM_INVALID` | addressFrom is invalid | Missing or invalid source address (required for DEX). |
| 422 | `ADDRESS_FROM_INVALID_FOR_CHAIN` | Chain-specific source address validation failure. `{type}` is "from" or "to". | |
| 422 | `ADDRESS_TO_IN_DEPOSIT_LOG` | addressTo cannot be used as it exists in deposit log | Address reuse prevention. |
| 422 | `ADDRESS_TO_IS_TOKEN_ADDRESS` | addressTo cannot be a token contract address | Sending to a contract address is blocked. |
| 422 | `X_ADDRESS_NOT_SUPPORTED` | X-Address not supported yet! | XRP X-format addresses not supported. |
| 422 | `REUSED_DEPOSIT_ADDRESS` | Deposit address collision detected. | |
| 422 | `SWAP_AMOUNT_IS_OUT_OF_BOUNDS` | Provider-level bounds exceeded at exchange time. | |
| 422 | `XMR_SWAP_AMOUNT_IS_OUT_OF_BOUNDS` | Private swap intermediary bounds exceeded. | |
| 422 | `TOKEN_DISABLED` | Token was disabled between quote and exchange. | |
| 422 | `INVALID_SWAP` | Unknown or disabled swap provider. | |
| 422 | `ORDER_ALREADY_INITIALIZING_OR_PROCESSED` | Order already INITIALIZING or processed | Duplicate exchange attempt. |
| 422 | `REFUND_ADDRESS_REQUIRED_FOR_FIXED_RATE` | Refund address is required for fixed rate exchanges | `refundAddress` was omitted when using a `fixed=true` quote. Always include it for fixed rate orders. |
| 422 | `FIXED_RATE_QUOTE_EXPIRED` | Fixed rate quote has expired | `validUntil` has passed, or the route lost fixed-rate capability after the quote was issued. On a private swap, either hop expiring is enough. Re-fetch a new quote. |
| 422 | `VALIDATION_ERROR` | The provided quote has expired, max age is `{N}` seconds | Quote freshness limit (60s by default), enforced separately from `validUntil`. Re-fetch the quote and submit immediately. |
| 422 | `UNABLE_TO_CREATE_REFUND_LEG` | Unable to create the refund leg | Private fixed rate only — the refund path could not be set up, so the order was not created and no funds moved. Retry, or fall back to a floating private swap. |
| 422 | `ADDRESS_REFUND_INVALID_FOR_CHAIN` | refundAddress is invalid for `{chain}` | `refundAddress` fails source-chain format validation, or exceeds the maximum length. |
| 422 | `ADDRESS_REFUND_SANCTIONED` | Refund address failed compliance screening | Collect a different refund address. |
| 422 | `INTERMEDIATE_REFUND_ADDRESS_REQUIRED` | — | The quote has `requiresIntermediateRefundAddress: true`. Send `intermediateRefundAddress`. See [Private Routes with an On-chain Hop](https://docs.houdini.com/developer-hub/swap-flows/private-onchain-hop.md). |
| 422 | `ONCHAIN_LEG_ROUTE_NOT_AVAILABLE` | — | For example, the feature was turned off after the quote was issued. Get a new quote. |
| 403 | `FIXED_RATE_NOT_ALLOWED` | Fixed-rate quotes are not enabled for this partner. | Account-level toggle. Contact your Houdini representative to enable it. |
| 403 | `FLOATING_RATE_NOT_ALLOWED` | Floating-rate quotes are not enabled for this partner. | Account-level toggle. Contact your Houdini representative to enable it. |
| 429 | `RATE_LIMIT_EXCEEDED` | (same pattern as quotes) | Includes `retryAfter` and `meta`. |
| 500 | `UNABLE_TO_PERFORM_EXCHANGE` | Unable to perform exchange, no available paths | All providers failed during execution. |
| 503 | `ANONYMOUS_DISABLED` | Anonymous exchanges are temporarily disabled... | Feature flag is off. |
| 404 | `NOT_FOUND` | Not found | Post-creation order lookup failed (edge case). |
| 500 | `INTERNAL_SERVER_ERROR` | Internal Server Error | Catch-all. |

### `GET /v2/orders/{houdiniId}` and `GET /v2/orders`

| HTTP | Code | Message | Notes |
| - | - | - | - |
| 404 | `NOT_FOUND` | Not found | Order doesn't exist or belongs to a different partner. |
| 422 | `VALIDATION_ERROR` | Validation Failed | Invalid query parameters. |
| 429 | `RATE_LIMIT_EXCEEDED` | (same pattern) | |
| 500 | `INTERNAL_SERVER_ERROR` | Internal Server Error | Catch-all. |

### Global Errors (all endpoints)

These can be returned by any endpoint:

| HTTP | Code | Message | Notes |
| - | - | - | - |
| 400 | `MISSING_API_CREDENTIALS` | Missing API credentials | No `Authorization` header or `partner-id` header provided. |
| 400 | `INVALID_API_CREDENTIALS_FORMAT` | Invalid API credentials format | `Authorization` header is malformed (expected `id:secret`). |
| 401 | `INVALID_API_CREDENTIALS` | Invalid API credentials | Credentials don't match any active partner. |
| 401 | `AUTHENTICATION_FAILED` | Authentication failed | Auth check threw an unexpected error. |
| 401 | `INVALID_SECURITY_SCHEME` | Invalid security scheme | Request used an unsupported authentication method. Internal safeguard — should not occur in normal usage. |
| 403 | `ACCESS_DENIED` | Access restricted for partner-id requests | Operation not allowed for public `partner-id` access. |
| 404 | `ROUTE_NOT_FOUND` | Route not found | The requested URL path doesn't match any v2 endpoint. |
| 429 | `RATE_LIMIT_EXCEEDED` | (dynamic — see special shape above) | Per-partner, per-operation rate limits. |
| 503 | `SERVICE_UNAVAILABLE` | Service Unavailable | Backend service is temporarily unavailable. |
| 500 | `INTERNAL_SERVER_ERROR` | Internal Server Error | Unhandled exception. |

***

## HTTP Status Summary

| Status | Meaning | Code Strings |
| - | - | - |
| 400 | Bad Request | `MISSING_API_CREDENTIALS`, `INVALID_API_CREDENTIALS_FORMAT` |
| 401 | Unauthorized | `INVALID_API_CREDENTIALS`, `AUTHENTICATION_FAILED`, `INVALID_SECURITY_SCHEME` |
| 403 | Forbidden | `ACCESS_DENIED` |
| 404 | Not Found | `NOT_FOUND`, `ROUTE_NOT_FOUND` |
| 422 | Unprocessable Entity | `VALIDATION_ERROR`, `QUOTE_OVER_LIMIT`, `AMOUNT_TOO_LOW`, `UNSUPPORTED_FROM_TOKEN`, `UNSUPPORTED_TO_TOKEN`, `TO_AND_FROM_CANNOT_BE_THE_SAME`, `UNSUPPORTED_ANON_TOKEN`, `SWAP_AMOUNT_IS_OUT_OF_BOUNDS`, `XMR_SWAP_AMOUNT_IS_OUT_OF_BOUNDS`, `ADDRESS_TO_INVALID`, `ADDRESS_TO_INVALID_FOR_CHAIN`, `ADDRESS_FROM_INVALID`, `ADDRESS_FROM_INVALID_FOR_CHAIN`, `ADDRESS_TO_IN_DEPOSIT_LOG`, `ADDRESS_TO_IS_TOKEN_ADDRESS`, `X_ADDRESS_NOT_SUPPORTED`, `REUSED_DEPOSIT_ADDRESS`, `TOKEN_DISABLED`, `INVALID_SWAP`, `ORDER_ALREADY_INITIALIZING_OR_PROCESSED`, `INVALID_QUOTE`, `INVALID_PATH`, `REFUND_ADDRESS_REQUIRED_FOR_FIXED_RATE`, `FIXED_RATE_QUOTE_EXPIRED`, `DEX_AND_FIXED_RATE_NOT_SUPPORTED`, `EXACT_OUT_REQUIRES_FIXED_RATE`, `EXACT_OUT_NOT_SUPPORTED_FOR_DEX`, `UNABLE_TO_CREATE_REFUND_LEG`, `ADDRESS_REFUND_INVALID_FOR_CHAIN`, `ADDRESS_REFUND_SANCTIONED` |
| 429 | Too Many Requests | `RATE_LIMIT_EXCEEDED` |
| 500 | Internal Server Error | `INTERNAL_SERVER_ERROR`, `UNABLE_TO_PERFORM_EXCHANGE` |
| 503 | Service Unavailable | `SERVICE_UNAVAILABLE`, `PRICE_QUOTES_NOT_RETRIEVED`, `ANONYMOUS_DISABLED` |

***

## Best Practices

1. **Match on `code`, not `message`** — The `code` field is a stable string enum. Messages may contain dynamic values and can change without notice.
2. **Use regex for dynamic messages** — For codes like `QUOTE_OVER_LIMIT`, `AMOUNT_TOO_LOW`, `SWAP_AMOUNT_IS_OUT_OF_BOUNDS`, extract the dynamic part with a regex:
   ```text theme={null}
   /Unable to perform exchange, quote over (\d+) USD/
   /Amount is too low, minimum is ([\d.]+) USD/
   ```
3. **Handle 422 as your primary error status** — Most business logic errors return 422, not 400 or 500.
4. **Respect `retryAfter`** — On 429 responses, wait the specified number of seconds before retrying. The `meta` object provides additional context about which limit was hit.
5. **Log `requestId`** — Always log the `requestId` from error responses. Include it when contacting support for faster debugging.
6. **Implement exponential backoff for 500/503** — These are transient errors. Retry with backoff (e.g. 1s, 2s, 4s) up to 3 attempts.
7. **Don't retry 422 errors** — These are deterministic. The same request will produce the same error. Fix the input before retrying.

## Need Help?

> [card] [API v2 Reference](https://docs.houdini.com/api-reference)
>
> Complete endpoint documentation

> [card] [Order Lifecycle](https://docs.houdini.com/developer-hub/core-concepts/order-lifecycle.md)
>
> Understand order states

> [card] [Quick Start](https://docs.houdini.com/developer-hub/getting-started/quick-start.md)
>
> Integration guides for each swap type

> [card] [Contact Support](https://docs.houdini.com/faqs/contact-support.md)
>
> Get help with specific issues


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