# Error Codes

Every error this API returns.

```json
{"code":-1121,"msg":"Invalid symbol."}
```

| Field | Type | Description |
| - | - | - |
| `code` | INT | Negative integer. **Stable — branch on this.** |
| `msg` | STRING | English text. Informational; wording may change. Never parse it. |
| `mt5RetCode` | INT | Present only when the trade server answered. Raw MT5 return code — see [Common Definition](/common-definition.md#mt5-request-return-codes-mt5retcode). |

When an order endpoint (`POST /v1/order`, `PUT /v1/order`, `DELETE /v1/order`, `PUT /v1/position`,
`DELETE /v1/position`, `POST /v1/batchOrders` items) recorded the operation and it was then
**rejected**, the error body carries three additional fields so the failure can be correlated without
a second lookup:

```json
{"code":-2018,"msg":"Insufficient margin on the trading account.","mt5RetCode":10019,"status":"REJECTED","clientOrderId":"bot-003","login":100123}
```

A request refused before anything was recorded (a validation, scope or rate-limit error) carries
only `code` and `msg` — plus, inside a `POST /v1/batchOrders` response, the item's `login` and
`clientOrderId` where they could be read.

New codes may be added at any time. Treat an unknown code in the `-10xx`/`-11xx` range as "my
request is wrong, do not retry", and an unknown code in the `-50xx` range as "execution problem,
status may be unknown".

## `-10xx` — General, server and network

Something went wrong that is not about the content of your request.

| Code | `msg` | HTTP | Notes |
| - | - | - | - |
| `-1000` | `Unknown error, please check your request or try again later. The execution status is UNKNOWN and could have been a success.` | 503 | **May have executed.** Confirm before retrying. See [the 503 semantics](/general-info.md#the-two-meanings-of-503). |
| `-1000` | `Service is currently unavailable, please try again later.` | 503 | Did not execute. Retry with backoff. Same code, different message — read `msg`. Also the stored outcome of an order the service could not queue within its dispatch window (`GET /v1/order` shows `REJECTED` with this code): it never executed, and retrying the **same** `newClientOrderId` sends it as a fresh order. |
| `-1001` | `Internal error; unable to process your request. Please try again.` | 500 | |
| `-1002` | `You are not authorized to execute this request.` | 401 | Missing or malformed `X-YBX-APIKEY` header. |
| `-1003` | `Too many requests; current limit is %s requests per minute. Please use the WebSocket streams for live updates to avoid polling.` | 429 | `Retry-After` carries the wait. A repeat breach escalates to HTTP 418. |
| `-1006` | `An unexpected response was received from the trade server. Execution status is UNKNOWN.` | 503 | Confirm before retrying. |
| `-1007` | `Timeout waiting for response from the trade server. Execution status is UNKNOWN.` | 504 | Confirm before retrying. |
| `-1008` | `Server is currently overloaded with other requests. Please try again in a few minutes.` | 503 | Did not execute. Also refuses a market-stream WebSocket connection over the per-IP or service-wide connection limit, before the upgrade. |
| `-1014` | `Unsupported order combination.` | 400 | The parameters are individually valid but cannot be combined. |
| `-1015` | `Too many new orders; current limit is %s orders per %s.` | 429 | The `ORDERS` bucket, not `REQUEST_WEIGHT`. |
| `-1016` | `This service is no longer available.` | 410 | A **retired** endpoint. Permanent; do not retry. |
| `-1017` | `Trading is temporarily disabled on this API. Retry later.` | 503 | The trading surface is switched off. Market data, market streams and account reads stay available. Retryable — retry with backoff, and alert if it persists. |
| `-1020` | `This operation is not supported.` | 400 | A plain HTTP request to a WebSocket URL, or an unknown WebSocket control `method`. |
| `-1021` | `Timestamp for this request is outside of the recvWindow.` | 400 | Clock drift, or a request that sat in a queue too long. Sync against `GET /v1/time`. |
| `-1022` | `Signature for this request is not valid.` | 401 | Almost always a `totalParams` mismatch — re-encoding after signing, or splitting parameters between query and body. |

## `-11xx` — Request and validation

Your request is malformed. Fix it; do not retry unchanged.

| Code | `msg` | HTTP |
| - | - | - |
| `-1100` | `Illegal characters found in a parameter.` | 400 |
| `-1101` | `Too many parameters sent for this endpoint.` | 400 |
| `-1102` | `A mandatory parameter was not sent, was empty/null, or malformed.` | 400 |
| `-1103` | `An unknown parameter was sent.` | 400 |
| `-1104` | `Not all sent parameters were read.` | 400 |
| `-1105` | `A parameter was empty.` | 400 |
| `-1106` | `A parameter was sent when not required.` | 400 |
| `-1111` | `Precision is over the maximum defined for this symbol.` | 400 |
| `-1115` | `Invalid timeInForce.` | 400 |
| `-1116` | `Invalid order type.` | 400 |
| `-1117` | `Invalid side.` | 400 |
| `-1118` | `New client order ID was empty.` | 400 |
| `-1119` | `Client order ID is too long or contains illegal characters.` | 400 |
| `-1120` | `Invalid interval.` | 400 |
| `-1121` | `Invalid symbol.` | 400 |
| `-1122` | `Invalid login.` | 400 |
| `-1125` | `This listenKey does not exist.` | 400 |
| `-1127` | `Lookup interval is too big.` | 400 |
| `-1128` | `Combination of optional parameters invalid.` | 400 |
| `-1130` | `Invalid data sent for a parameter.` | 400 |
| `-1131` | `batchOrders must contain between 1 and 100 items.` | 400 |
| `-1132` | `Duplicate client order ID in batch.` | 400 |
| `-1133` | `Client order ID was already used by a different operation.` | 400 |

`-1102` covers a missing `timestamp`; a `timestamp` or `recvWindow` that is present but not an
integer — or a `recvWindow` outside `1`–`60000` — is `-1130`. A present-but-unparseable numeric or
decimal parameter, a `limit` outside its range and an unknown `newOrderRespType` are `-1130` too.

`-1106` is returned when `timeInForce` is sent on a `MARKET` order, or `stopLimitPrice` on an order
that is not a stop-limit.

`-1121` is also what a WebSocket `SUBSCRIBE` returns for a stream name whose symbol does not exist —
including one that differs only in **case**.

`-1122` means the `login` value is not a valid int64. A login that exists but is not yours is
`-2022`.

`-1133` is returned on **every** endpoint that takes `newClientOrderId` — including
`POST /v1/order` and each `POST /v1/batchOrders` item (as the error object in that item's slot) —
when your key already used that id for a different kind of operation **or for a different `login`**.
Nothing is executed and nothing is replayed.

## `-20xx` — Processing and authorization

The request is well-formed but cannot be carried out.

| Code | `msg` | HTTP | Notes |
| - | - | - | - |
| `-2010` | `New order was rejected.` | 400 | Generic placement rejection where no more specific code applies. |
| `-2011` | `Cancel was rejected.` | 400 | |
| `-2013` | `Order does not exist.` | 400 | Also returned for an order belonging to a different login — never a permission error. |
| `-2014` | `API-key format invalid.` | 401 | |
| `-2015` | `Invalid API-key, IP, or permissions for action.` | 401 | Unknown key, revoked key, source IP not allowlisted, or a read-only key on a `TRADE` endpoint. Deliberately not distinguished. |
| `-2018` | `Insufficient margin on the trading account.` | 400 | MT5 `10019`. |
| `-2021` | `Order would immediately trigger.` | 400 | A pending price on the wrong side of the market. |
| `-2022` | `Login is not in this API key's scope, or the account is not tradable.` | 400 | Deliberately ambiguous: not in this key's scope, does not exist, the owner is inactive, or the account is not active. A key cannot probe for logins it does not own. **Not** returned merely because trading is disabled — that is `-2024`. |
| `-2023` | `Position does not exist.` | 400 | Also returned for a position on a different login. |
| `-2024` | `Trading is disabled for this account.` | 400 | The account exists and is in scope, but trading on it is forbidden. Returned by the operations that TRADE; reads such as `GET /v1/account` still succeed and report `tradeAllowed: false`. |

## `-40xx` — Filters and symbol rules

The request violates a symbol specification or a group limit. Every one of these is checkable
against `GET /v1/exchangeInfo` before you send.

| Code | `msg` | HTTP | `mt5RetCode` |
| - | - | - | - |
| `-4001` | `Price less than 0.` | 400 | |
| `-4002` | `Invalid price.` | 400 | 10015 |
| `-4003` | `Volume less than 0.` | 400 | |
| `-4004` | `Invalid volume.` | 400 | 10014 |
| `-4005` | `Volume greater than volumeMax for this symbol.` | 400 | |
| `-4006` | `Volume less than volumeMin for this symbol.` | 400 | |
| `-4007` | `Volume is not a multiple of volumeStep for this symbol.` | 400 | |
| `-4008` | `Price is not a multiple of tickSize for this symbol.` | 400 | |
| `-4009` | `Stop levels are too close to market.` | 400 | 10016 |
| `-4010` | `Market is closed for this symbol.` | 400 | 10018 |
| `-4011` | `Trading is disabled for this symbol.` | 400 | 10017 |
| `-4012` | `Invalid order expiration.` | 400 | 10022 |
| `-4013` | `Only long positions are allowed for this symbol.` | 400 | 10042 |
| `-4014` | `Only short positions are allowed for this symbol.` | 400 | 10043 |
| `-4015` | `Only position closing is allowed for this symbol.` | 400 | 10044 |
| `-4016` | `This order type is not allowed for this symbol.` | 400 | |
| `-4017` | `Fill mode is not supported for this symbol.` | 400 | 10030 |
| `-4018` | `Order limit reached for this account.` | 400 | 10033 |
| `-4019` | `Position limit reached for this account.` | 400 | 10040 |
| `-4020` | `Volume limit reached for this account.` | 400 | 10034 |
| `-4021` | `Order or position is too close to market to be modified.` | 400 | 10029 |
| `-4022` | `Hedge positions are prohibited for this account.` | 400 | 10046 |
| `-4023` | `Position closure is not allowed by the FIFO rule.` | 400 | 10045 |
| `-4024` | `Volume to close exceeds the current volume of the position.` | 400 | 10038 |
| `-4025` | `An order to close this position already exists.` | 400 | 10039 |

`-4005`, `-4006`, `-4007`, `-4008` and `-4009` are raised **before** the trade server is contacted
where the service can determine them from the symbol specification, so they usually carry no
`mt5RetCode`. The same violation caught by the server carries one.

## `-50xx` — Execution

The trade server was reached and something happened during execution. These are the codes where
`mt5RetCode` is normally present.

| Code | `msg` | HTTP | `mt5RetCode` | Did it execute? |
| - | - | - | - | - |
| `-5001` | `Requote.` | 400 | 10004 | No. Re-price and resend with a new client order id. |
| `-5002` | `Price has changed.` | 400 | 10020 | No. |
| `-5003` | `No price available for this symbol.` | 400 | 10021 | No. |
| `-5004` | `Order has been changed.` | 400 | 10023 | No — your modification raced another change. Re-read and retry. |
| `-5005` | `Request does not contain changes.` | 400 | 10025 | No. |
| `-5006` | `Position is already closed.` | 400 | 10036 | No — it was already gone. |
| `-5007` | `The trade server rejected the request.` | 400 | 10006, 10011 | No. |
| `-5008` | `The trade server did not answer in time. Execution status is UNKNOWN — confirm via the user data stream or GET /v1/order before retrying.` | 504 | 10012 | **Unknown.** |
| `-5009` | `Execution status is UNKNOWN. Confirm via the user data stream or GET /v1/order before retrying.` | 503 | | **Unknown.** Retrying the same `newClientOrderId` is safe; a new one is not. |
| `-5010` | `Request canceled by the trade server.` | 400 | 10007 | No. |
| `-5011` | `The trade server is rejecting new requests. Retry shortly.` | 503 | 10024 | No. Back off. |
| `-5012` | `Autotrading is disabled on the trade server.` | 400 | 10026, 10027 | No. Operator action required. |
| `-5013` | `Request blocked by the dealer.` | 400 | 10028 | No. |
| `-5014` | `Allowed only for real accounts.` | 400 | 10032 | No. |
| `-5015` | `No connection to the trade server.` | 503 | 10031 | No. Retry with backoff. Also returned when a **read** cannot reach the trade server — an uncached market-data or account snapshot — in which case there is no `mt5RetCode`; and by every TRADE endpoint, `POST /v1/batchOrders` included, when the execution path is not running, before any order is created. |
| `-5016` | `Invalid or prohibited order type.` | 400 | 10035 | No. |
| `-5017` | `Invalid request.` | 400 | 10013 | No. |
| `-5018` | `Request rejected, order canceled.` | 400 | 10041 | No — a broker routing rule cancelled it. |

### Reading `-5008` and `-5009`

These two are the only codes that mean **"we do not know"**. Everything else is definitive.

`-5009` also covers a failure to write the order to the execution queue: the write may have landed
before the error surfaced, so the operation is recorded as still open rather than rejected, and the
order can still fill. Retry it the same way as any other `-5009` — identically, same
`newClientOrderId`. The same holds for an internal fault in the trade executor after the request
may have been sent: the order is recorded `IN_DOUBT` with `-5009`, never as a retryable `-1000`.

The safe recovery is identical for both:

1. Retry the **identical** request with the **same** `newClientOrderId`. Idempotency guarantees you
   get the stored outcome, not a second order.
2. Or, if you did not set a client order id, call `GET /v1/positions` and
   `GET /v1/userTrades` and reconcile before doing anything else.

Never respond to `-5008` or `-5009` by sending the same order with a **new** client order id. That
is how a position gets opened twice.

## Reserved codes

These codes are part of the catalogue so a client can model them, but the current implementation
does not return them: `-1000` with the "execution status is UNKNOWN" message (unknown outcomes are
reported as `-5008` / `-5009`), `-1006`, `-1007`, `-1014`, `-1103`, `-1104`, `-1105`, `-1111`,
`-2011` and `-2014` (an unrecognised key of any format is `-2015`). Handle them as their family
describes; they may start appearing without notice.

## Quick index

| Range | Family | Retry? |
| - | - | - |
| `-1000`, `-1006`, `-1007` | Unknown execution status | Only with the same client order id, after confirming |
| `-1001`, `-1008`, `-1017` | Server-side, no execution | Yes, with backoff |
| `-1003`, `-1015` | Rate limit | After `Retry-After` |
| `-1002`, `-1021`, `-1022`, `-2014`, `-2015` | Authentication | No — fix credentials or clock |
| `-11xx` | Malformed request | No — fix the request |
| `-2013`, `-2022`, `-2023` | Not found / not in scope | No |
| `-2010`, `-2018`, `-2021`, `-2024` | Rejected, did not execute | Not unchanged — fix the cause first |
| `-40xx` | Symbol or group rule | No — check `GET /v1/exchangeInfo` |
| `-5001` … `-5007`, `-5010` … `-5018` | Execution, definitively did not happen | Yes, as a new order |
| `-5008`, `-5009` | Execution status unknown | Only with the same client order id |
