Guides / Reference
Errors and retcodes
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": "..." }. Checkerror, notmessage(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) and504 engine_timeout. After those, a trade's outcome is unknown: retry it with the sameIdempotency-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_connectedwithkill_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, positions and pending orders, and engage it again: engaging again retries whatever is left (it takes noIdempotency-Key). - MT5's answer to a trade is
200with"state": "done", whether MT5 filled the order or rejected it. Readresult.retcode:10009is done;10008is 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 (thenresult.commentis the terminal's), sodonedoesn't prove the broker saw it.
{
"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, or wait for its request event on the stream. |
401 | Check the key: see 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 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 and 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. |