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

# Errors

> Read an error's status, code and parameters, so your client knows whether to retry, fix the request or tell the trader.

Every error is JSON with an `error` field, and on most routes that field is a sentence. This page explains the shapes an error takes, the codes
a refused request carries, and what each HTTP status means. Use it when you write the error
handling for your integration.

```json theme={null}
{ "error": "clientOrderId required" }
```

The `error` text is written for a person reading a log. Show it, log it, and decide what to do from
the status and the `code`. Some routes add fields you should keep, such as `code`, `params`,
`commandId`, `referenceId` or the details of a conflict, because they tell you whether a retry is
safe.

## Error shapes

| Where | Shape | Example |
| - | - | - |
| Trading routes | `error` is a sentence, beside a `code` and its `params` | `{ "error": "…", "code": "risk_locked", "params": { "holds": ["risk_uncovered"] } }` |
| Account-issuing and balance routes | `error` is a sentence, beside a `code` and its `params` | `{ "error": "…", "code": "amount_precision_exceeded", "params": { "currency": "USDC", "decimals": 6 } }` |
| Venue routes | `error` is the code itself, sometimes with `field` or `params` | `{ "error": "loss_limit_unenforced", "field": "group.risk.dailyLossLimit" }` |

## Trading refusals

When trdrs refuses a trading request before anything reaches the market, the answer carries the
refusal's `code` and `params`. For example, a futures account at Binance held in Hedge Mode is
refused like this:

```json theme={null}
{
  "error": "Binance reports this futures account in Hedge Mode. trdrs trades One-way Mode only; switch the position mode to One-way in Binance to trade here.",
  "code": "hedge_position_mode",
  "params": { "provider": "binance", "instrument": null, "setting": "position_mode", "reported": "hedge", "supported": ["one_way"] }
}
```

`code` is one of the codes in the `Refusal` model of the API reference. They fall into these
groups:

| Refused by | Codes include |
| - | - |
| The provider's account settings | `hedge_position_mode`, `multi_assets_mode`, `portfolio_margin` and the rest of the account-mode codes |
| The paper book's product rules and your venue's conditions | `instrument_not_permitted`, `order_quantity_above_limit`, `entry_halted`, `venue_route_missing` and more |
| A hold on the account | `risk_locked`, with the holds in `params.holds` |
| A missing price on an issued account | `market_data_unavailable`, when the only hold is an instrument whose price hasn't arrived yet |
| A close-only account | `stage_version_unrecorded` |
| An account held for reconciliation | `reconciliation_hold` |
| The live-trading switch | `feature_disabled` |
| The paper book's own checks on an issued account | `not_found`, `permission_denied`, `invalid_request`, `version_conflict`, `idempotency_conflict`, `position_mode_unsupported`, `position_not_closable`, `position_revision_stale`, `product_economics_unsupported` and `currency_economics_unsupported`, with no parameters |
| The engine's own admission | `outcome_unknown`, `engine_draining`, `engine_overloaded` and the rest |

Every account a venue issues is an issued account here. Any other rejection, including the
provider's own, is `order_rejected`, and its `error` is the sentence to show.

`params` carries only what your client needs to say the same thing in its own words: a provider's
name, an instrument, a mode or a limit. It never carries a secret, an account number or text a
provider sent.

Translate a refusal from its `code` and `params`, keep `error` as the English fallback, and show a
general message for a code your client doesn't know, because the list grows.

<Tip>
  [`GET /api/trading/actions`](/api-reference/trading/get-what-the-account-may-do) answers with the
  same refusals before an order is sent. Use it to disable what would be refused and say why, while
  what stays open, such as cancels and flattening under a hold, stays enabled. It's advisory: the
  order is judged again when it's sent.
</Tip>

## Account and balance errors

The account-issuing and balance routes refuse these before anything is created or recorded. They
aren't trading refusals and aren't in the `Refusal` model.

```json theme={null}
{
  "error": "amount has more than 2 decimals, the precision of a USDC account; nothing was recorded",
  "code": "amount_precision_exceeded",
  "params": { "currency": "USDC", "decimals": 2 }
}
```

| Code | Status | When | `params` | What to do |
| - | - | - | - | - |
| `amount_precision_exceeded` | `400` | A credit, debit or adjustment has more decimals than the account's currency is posted in | `currency`, and `decimals`: 2 for USD; 6 for USDC on an issued account and 2 otherwise; `null` for a currency the paper book holds no money in | Correct the amount and send the same reference again |
| `balance_refused` | `409` | A debit on an issued account can't be made. A debit there withdraws free collateral | `reason`: `insufficient_funds` (less free collateral than the amount), `permission_denied` (a hold is on the account) or `invalid_request` (a position can't be valued right now) | Send the same reference again once the cause clears |
| `currency_not_issuable` | `400` | An issue names a currency this deployment doesn't issue: USDC outside the sandbox, or anything but USD and USDC. A batch is refused whole | `currency`, and `environment`: `sandbox` or `production` | Issue in USD, or USDC in the sandbox |

The venue routes answer `currency_not_issuable` and `amount_precision_exceeded` in their own shape,
with the code as `error` beside the same `params`.

While an account is held for reconciliation, no money moves on it, not even a credit. A balance
operation is refused with `423` and `reconciliation_hold`, and records nothing, until a trdrs
operator releases the hold.

## Venue configuration errors

The venue routes refuse a configuration they won't store, with the code as `error`. Nothing is
recorded.

| Code | Status | When | What to do |
| - | - | - | - |
| `idempotency_key_required` | `400` | A write has no `Idempotency-Key` header, or one longer than 128 characters | Send a key; see [Idempotency](/api/idempotency) |
| `loss_limit_unenforced` | `400` | A profile, group override, preview or account override states `dailyLossLimit` or `weeklyLossLimit` while an account the venue issued isn't bound to its published risk policy, such as one held at a provider. Nothing would enforce the limit on that account | Send `null`, or leave the field out of an override |
| `margin_policy_superseded` | `400` | A profile, group override or preview states a `margin` policy. Margin comes from your published risk policy | Send `margin: null`, or leave it out of an override |
| `financing_superseded` | `400` | A profile, group override or preview states `financing`. Financing comes from your published risk policy | Leave the field out |
| `unsupported_value` | `400` | A commission leaves out its `timing`, its `direction` or, per unit, its `unit`. A venue condition's per-unit `unit` is `quantity` | State the field `field` names |
| `round_turn_is_both` | `400` | A `round_turn` commission is stated for one direction only. A round turn charges the closing fill for both sides | State it with `direction: "both"` |
| `entitled_source_unserved` | `400` | An instrument names an `entitlement` whose `pricing.sourceId` isn't a source trdrs serves from each trader's own feed (`rithmic` today; see [Providers](/providers)), so no account holding it could be valued. Checked when the instrument is saved and when it's activated | Use a served source |
| `risk_policy_owner_conflict` | `409` | The policy id belongs to another venue or to trdrs, or the policy's authority names another venue | Use your own policy id, with your venue as its authority |
| `risk_policy_id_reserved` | `409` | The policy id starts `trdrs-` | Choose another id |
| `risk_policy_version_conflict` | `409` | Other content is sent under a version already stored | Store the change as a new version |
| `risk_policy_unsourced` | `409` | A version with a figure marked unsourced is published | Source the figure, store a new version and publish that |
| `risk_policy_posting_mismatch` | `409` | The posting term states a currency at a precision the paper book doesn't post it at: USD in 2 decimals, USDC and USDT in 6 | Correct the posting term |
| `risk_policy_unchanged` | `409` | The version is already in force | Nothing to do |

The fields a venue route refuses are named in `field`:

```json theme={null}
{ "error": "margin_policy_superseded", "field": "group.margin" }
```

A risk policy the venue doesn't state is `404 not_found`, for a read and for a publication alike.

## Status codes

| Status | Meaning |
| - | - |
| `400` | The request is malformed: an unparseable timeframe, an unknown `broker` or `account`, an invalid cursor. It always fails closed and never falls back to a default. |
| `401` | No credential resolved: the key is missing, malformed, expired or revoked, and the origin isn't registered. |
| `402` | Challenges only: the program has a non-zero `priceCents` and this deployment collects no fee. |
| `403` | On a browser request, `origin not allowed`: the origin check in front of the trading and account routes, not a verdict on your key. On the order and reverse routes, `feature_disabled`: live trading is off for this user, and every order on a real provider account is refused, a reducing one included. On a venue route, the key lacks the scope the route needs. |
| `404` | The resource doesn't exist or isn't yours. On `/api/market/symbol-info`, an unknown symbol. On Challenges, also the answer when challenges aren't enabled for the deployment. |
| `409` | The request conflicts with what already happened. The `clientOrderId` was already used and the earlier order stands; a saved exit plan was edited since your preview (`exit_plan_conflict`); the position changed under a close (`position_changed`). A hold refused a command that can't prove it reduces exposure (`risk_locked`), such as a close the provider doesn't enforce as reduce-only. An earlier command on the account has an unconfirmed outcome (`command_blocked`) or was already settled (`command_settled`). A command used the reserved safety capacity without reducing exposure (`safety_lane_refused`). On Challenges enrollment, an attempt is already in flight. |
| `413` | The body is too large. Balance operations accept at most 16 KiB. Make the body smaller before you retry. |
| `415` | The route needs a JSON body with `Content-Type: application/json`. |
| `422` | The request is well formed but can't take part yet. On a trading route it's a refusal with its `code`: the provider, the account's mode, the paper book or your venue's conditions refused the order, and nothing was sent. On an issued account whose only hold is an instrument whose price hasn't arrived yet, an order adding to that instrument alone is `market_data_unavailable` with `params.cause` `mark_missing`. An order adding to any other instrument is still `423` `risk_locked`, and so is every addition while another hold is on the account. An account whose risk decisions haven't read any price yet can also answer `423` `risk_locked` there, when its terms have an effective window or a day margin. |
| `423` | The account is held. With `risk_locked`, `params.holds` names the holds: the order route refuses an order that may add exposure and accepts a reduce-only one, while the exits, reverse and replace routes refuse every order. With `reconciliation_hold`, trdrs found the account's book doesn't follow from what happened to it: orders that may add exposure, credits, debits and resets are refused until a trdrs operator releases the hold, and nothing on the book is rewritten. With `stage_version_unrecorded`, the account is close-only because its running cycle can't prove which stage rules judge it, until the version is established or you reset it; `params.reason` and `params.stageId` say why. Cancels and flattens stay available in every case, and so do reductions and closes, except a close the provider doesn't enforce as reduce-only while `risk_locked` holds. |
| `429` | You're rate limited. Wait for `Retry-After`. |
| `500` | The server couldn't complete the request. Retry reads with backoff, and never treat missing eligibility data as a pass. |
| `502` | A read from the provider failed partway through an account snapshot. A partial snapshot is never served, because a half-read account can't be told apart from a complete picture of an emptier one. Read it again. |
| `503` | On the trading routes, a refusal before anything was sent carries `Retry-After: 1`: the account's engine couldn't be reached (`ownership_unavailable`, `owner_unavailable`), the engine is draining (`engine_draining`) or shedding orders (`engine_overloaded`), the account's owner couldn't be confirmed this second (`account_not_owned`, `account_unresolved`), or the account's holds or the live-trading switch couldn't be read. The one trading `503` without `Retry-After` is `outcome_unknown`: the order was sent and may be resting at the provider. On the Legacy Partner API balance operation every `503` carries `Retry-After: 1`, `outcome_unknown` included. Elsewhere, an upstream feed is unconfigured or down, or a futures feed is refused with a typed reason. |

## Handle the statuses that need care

* **`409` means read the response.** A duplicate order means your intent already landed; a
  conflict means your change was refused. Sending the same `clientOrderId` again keeps answering
  `409`, and sending a new one would place a second order. Read the account stream to see what
  actually rests.
* **`503` doesn't prove nothing happened.** A trading `503` with `Retry-After` was refused before
  it was sent: wait that many seconds and retry with the same `clientOrderId`. `outcome_unknown`
  means the effect may have happened, whatever the headers say: read the account stream, or the
  balance operation's receipt by its original reference. Never mint a new id to get past it.
* **`423` means the account is held, not that the request was wrong.** Show it to the trader as a
  risk hold. Retrying in a loop earns the same refusal.
* **`429` tells you how long to wait.** Wait for `Retry-After` rather than retrying at once.

A request with an `Origin` header must come from a registered origin, even with a valid key
attached. [Keys and authentication](/api/keys#browser-access-to-market-data) has the rule.
