# Errors and retcodes

Source: https://docs.pipsocket.com/errors

The API's refusals, MT5's retcodes, and what to do about each.

Two kinds of answers say something went wrong, and they mean different things:

- **A refusal** is an HTTP error with `{ "error": "<code>", "message": "..." }`. Check `error`, not `message` (its wording may change). Almost every refusal means nothing was done. Two mean the call may have reached the account's host before the answer was lost: `503 engine_unavailable` (the host disconnected while the call waited) and `504 engine_timeout`. After those, a trade's outcome is unknown: retry it with the **same** `Idempotency-Key`, which returns the first request if the host recorded it, and sends it if not.
- **The kill switch** is the exception. Engaging it while the terminal is away answers `503 account_not_connected` with `kill_switch`: the switch is engaged all the same (it blocks new exposure, and survives restarts), and pending orders may already have been deleted if the terminal stopped answering part way. Read [the kill switch](/api/kill-switch), positions and pending orders, and engage it again: engaging again retries whatever is left (it takes no `Idempotency-Key`).
- **MT5's answer** to a trade is `200` with `"state": "done"`, whether MT5 filled the order or rejected it. Read `result.retcode`: `10009` is done; `10008` is placed (a pending order); the rest are partial fills or rejections. A rejection can come from the trade server, or from the terminal refusing to send the request at all (then `result.comment` is the terminal's), so `done` doesn't prove the broker saw it.

```json
{
  "request": "req_5f0c2a9e41d7b3a8c6e2f190",
  "state": "done",
  "result": { "retcode": 10016, "retcode_name": "TRADE_RETCODE_INVALID_STOPS", "comment": "Invalid stops", "...": "..." },
  "message": "The stop loss or take profit is invalid for this symbol or too close to the price.",
  "sent": { "...": "..." }
}
```

## What to do

| Answer | Do |
| --- | --- |
| `200` with `"state": "done"` | Read `result.retcode`. A rejection is final: nothing more happens to that request. Change it before sending it again (with a new `Idempotency-Key`). |
| `202` with `"state": "pending"` | Don't send it again. [Look it up](/api/look-up-a-request), or wait for its `request` event on the [stream](/websocket). |
| `401` | Check the key: see [Authentication](/authentication). |
| `403` with `error code: 1010` | Cloudflare refused your client's `User-Agent` before the API saw the call (Python's `urllib` sends one it refuses). Set your own `User-Agent`, or use a client such as `requests`. |
| `404` | Check the account id, ticket or symbol. Closing, modifying and setting stops read the position or order fresh first: one that's gone is `404`. |
| `422` | Fix the field `message` names. Nothing was sent. |
| `423 kill_switch_engaged` | The account's [kill switch](/api/kill-switch) is engaged: lift it to open new exposure. |
| `503`, `504` | Retry a trade call with the **same** `Idempotency-Key`, never a new one: if the first call was recorded you get it back, and nothing is sent twice. Most are refused before anything is sent, but after `engine_unavailable` or `engine_timeout` the first call may have been sent. A trade sent without a key can't be retried safely: check [positions](/api/positions) and [history](/api/history) first. |

## Error codes

Every code the API answers with. "From" says who refuses: the API, before the account's host is reached, or the engine on the host.

| Status | Code | From | Meaning |
| --- | --- | --- | --- |
| 401 | `missing_api_key` | API | No API key: send it as `Authorization: Bearer <key>`. |
| 401 | `invalid_api_key` | API | The key is not a workspace's current key (a rolled key stops working within about a minute). |
| 404 | `no_such_account` | Engine | No such account, or one the key's workspace doesn't have: the two look the same. |
| 404 | `no_such_order` | Engine | The account has no pending order with that ticket (read fresh from the terminal). |
| 404 | `no_such_position` | Engine | The account has no open position with that ticket (read fresh from the terminal). |
| 404 | `no_such_request` | Engine | The account has no request with that id (requests are kept 7 days). |
| 404 | `no_such_symbol` | Engine | The broker has no symbol by that name. |
| 422 | `invalid_request` | Engine | A field, query parameter or header is missing, malformed or not allowed, or the body is not JSON. Nothing was sent. |
| 422 | `range_too_large` | Engine | The history range holds more than one answer can carry: ask for a shorter range. |
| 423 | `kill_switch_engaged` | Engine | The account's kill switch is engaged: new orders and changes to pending orders are refused until it is lifted. Nothing was sent. |
| 503 | `account_not_connected` | Engine | The account's terminal is not connected to the engine (stopped, starting, or restarting). Nothing was sent, except by the kill switch: engaging it still engages it, and may already have deleted pending orders. |
| 503 | `bridge_outdated` | Engine | The account's terminal runs an older bridge that can't trade: restart the account to update it. Nothing was sent. |
| 503 | `account_restarting` | Engine | The account's terminal is being replaced. Nothing was sent; try again in a minute. |
| 503 | `bad_command` | Engine | The terminal refused the command, for example a negative `expiration`, or a history range MT5 can't select. |
| 503 | `terminal_error` | Engine | The terminal did not give the answer asked for. |
| 503 | `symbol_too_large` | Engine | One symbol's entry is too large for the terminal to list. |
| 503 | `no_price` | Engine | The terminal has no price for the symbol yet (it waited 2 seconds). |
| 503 | `engine_busy` | Engine | The account's engine host has too many calls in flight (256). Nothing was done; try again. |
| 503 | `engine_unavailable` | API | The account's engine host is not connected to Pipsocket, or disconnected while the call waited. A trade it already had is still settled: look it up. |
| 504 | `terminal_timeout` | Engine | The account's terminal did not answer within 5 seconds. |
| 504 | `engine_timeout` | API | The account's engine did not answer within 30 seconds. |

A trade call's `503` can also be a stored request the terminal didn't send (`"state": "refused"`, with `result.error` saying why, such as `busy` when 1,000 trades are already waiting for the trade server). A ticket in the path that isn't a whole number answers `400` with no body.

## MT5 retcodes

MT5's trade server return codes (`MqlTradeResult.retcode`), with the names the API sends in `retcode_name`. A code MT5 doesn't document comes back as `UNKNOWN`.

| Code | Name | Meaning |
| --- | --- | --- |
| 0 | `OK` | The request passed the check. |
| 10004 | `TRADE_RETCODE_REQUOTE` | The broker requoted the price. |
| 10006 | `TRADE_RETCODE_REJECT` | The trade server rejected the request. |
| 10007 | `TRADE_RETCODE_CANCEL` | The request was cancelled by the trader. |
| 10008 | `TRADE_RETCODE_PLACED` | The order was placed; execution is not confirmed yet. |
| 10009 | `TRADE_RETCODE_DONE` | The request was completed. |
| 10010 | `TRADE_RETCODE_DONE_PARTIAL` | Only part of the request was completed. |
| 10011 | `TRADE_RETCODE_ERROR` | The trade server could not process the request. |
| 10012 | `TRADE_RETCODE_TIMEOUT` | The request was cancelled by a timeout. |
| 10013 | `TRADE_RETCODE_INVALID` | The request is invalid. |
| 10014 | `TRADE_RETCODE_INVALID_VOLUME` | The volume is invalid for this symbol. |
| 10015 | `TRADE_RETCODE_INVALID_PRICE` | The price is invalid. |
| 10016 | `TRADE_RETCODE_INVALID_STOPS` | The stop loss or take profit is invalid for this symbol or too close to the price. |
| 10017 | `TRADE_RETCODE_TRADE_DISABLED` | Trading is disabled. |
| 10018 | `TRADE_RETCODE_MARKET_CLOSED` | The market is closed. |
| 10019 | `TRADE_RETCODE_NO_MONEY` | There is not enough money for the request. |
| 10020 | `TRADE_RETCODE_PRICE_CHANGED` | The price changed. |
| 10021 | `TRADE_RETCODE_PRICE_OFF` | There are no quotes to process the request. |
| 10022 | `TRADE_RETCODE_INVALID_EXPIRATION` | The order expiration is invalid. |
| 10023 | `TRADE_RETCODE_ORDER_CHANGED` | The order changed state. |
| 10024 | `TRADE_RETCODE_TOO_MANY_REQUESTS` | Too many requests in a short time. |
| 10025 | `TRADE_RETCODE_NO_CHANGES` | The request changes nothing. |
| 10026 | `TRADE_RETCODE_SERVER_DISABLES_AT` | Automated trading is disabled by the server. |
| 10027 | `TRADE_RETCODE_CLIENT_DISABLES_AT` | Automated trading is disabled in the terminal. |
| 10028 | `TRADE_RETCODE_LOCKED` | The request is locked for processing. |
| 10029 | `TRADE_RETCODE_FROZEN` | The order or position is frozen. |
| 10030 | `TRADE_RETCODE_INVALID_FILL` | The filling mode is not allowed for this symbol. |
| 10031 | `TRADE_RETCODE_CONNECTION` | There is no connection to the trade server. |
| 10032 | `TRADE_RETCODE_ONLY_REAL` | This is only allowed for live accounts. |
| 10033 | `TRADE_RETCODE_LIMIT_ORDERS` | The account has reached its limit of pending orders. |
| 10034 | `TRADE_RETCODE_LIMIT_VOLUME` | The symbol has reached its volume limit for orders and positions. |
| 10035 | `TRADE_RETCODE_INVALID_ORDER` | The order type is incorrect or not allowed. |
| 10036 | `TRADE_RETCODE_POSITION_CLOSED` | The position is already closed. |
| 10038 | `TRADE_RETCODE_INVALID_CLOSE_VOLUME` | The close volume is more than the position's volume. |
| 10039 | `TRADE_RETCODE_CLOSE_ORDER_EXIST` | A close order already exists for the position. |
| 10040 | `TRADE_RETCODE_LIMIT_POSITIONS` | The account has reached its limit of open positions. |
| 10041 | `TRADE_RETCODE_REJECT_CANCEL` | The pending order's activation was rejected and the order cancelled. |
| 10042 | `TRADE_RETCODE_LONG_ONLY` | Only long positions are allowed for this symbol. |
| 10043 | `TRADE_RETCODE_SHORT_ONLY` | Only short positions are allowed for this symbol. |
| 10044 | `TRADE_RETCODE_CLOSE_ONLY` | Only closing positions is allowed for this symbol. |
| 10045 | `TRADE_RETCODE_FIFO_CLOSE` | Positions may only be closed first in, first out. |
| 10046 | `TRADE_RETCODE_HEDGE_PROHIBITED` | Opposite positions on one symbol are not allowed. |

