Error Handling¶
syntiq-mt5 never raises exceptions for operational failures. All errors are returned as Result values.
How MT5 errors work¶
The raw MetaTrader5 library has a critical design flaw: it stores the last error in global mutable state.
The problem with last_error()¶
import MetaTrader5 as mt5
# Call 1: fails
mt5.symbol_info("INVALID")
# Call 2: succeeds
mt5.symbol_info("EURUSD")
# Now check the error
error = mt5.last_error()
print(error) # (1, 'Success') ❌ The real error is gone!
What went wrong:
1. First call fails → MT5 stores error code 4301 ("Symbol not found")
2. Second call succeeds → MT5 overwrites the error with (1, 'Success')
3. When you check last_error(), you get the wrong result
Why this is dangerous¶
# ❌ WRONG: Error is lost
res1 = mt5.symbol_info("INVALID") # Fails
res2 = mt5.symbol_info("EURUSD") # Succeeds
error = mt5.last_error() # Returns success!
# ❌ WRONG: Race condition in concurrent code
res = mt5.positions()
# Another thread calls mt5.account_info() here
error = mt5.last_error() # Wrong error!
# ❌ WRONG: Forgot to check immediately
res = mt5.positions()
do_some_processing()
error = mt5.last_error() # Might be overwritten
How syntiq-mt5 solves this¶
syntiq-mt5 captures last_error() immediately after every operation and attaches it to the returned Result. The error is bound to the operation that produced it.
from syntiq_mt5 import MetaTrader5Client
mt5 = MetaTrader5Client()
# Each Result carries its own error
res1 = mt5.symbol_info("INVALID") # res1.error_code = 4301
res2 = mt5.symbol_info("EURUSD") # res2.error_code = None
# Errors are preserved
print(res1.error_code) # 4301 ✅
print(res1.error_message) # "Symbol not found" ✅
print(res2.success) # True ✅
Benefits:
- ✅ No race conditions
- ✅ No lost errors
- ✅ No manual last_error() calls
- ✅ Errors are immutable and bound to their operation
- ✅ Safe in concurrent code (each Result is independent)
You never need to call last_error() yourself
The SDK handles it automatically. Every Result contains the error that occurred during that specific operation.
Reading errors¶
res = mt5.login(creds)
if not res.success:
print(f"operation: {res.operation}") # "login"
print(f"context: {res.context}") # MT5 API function name
print(f"error code: {res.error_code}") # e.g. 10013
print(f"message: {res.error_message}")
SDK-internal errors¶
Some errors originate inside the SDK, not from MT5. These use negative error codes:
SDK error codes
| Code | Meaning |
|---|---|
-10 |
initialize() was not called before this operation |
mt5 = MetaTrader5Client()
res = mt5.positions()
# res.success == False
# res.error_code == -10
# res.error_message == "Client not initialized. Call initialize() first."
Handling specific errors¶
from syntiq_mt5 import constants
res = mt5.order_send(request)
if not res.success:
# SDK-level failure (connection, not initialized, etc.)
print(f"SDK error {res.error_code}: {res.error_message}")
elif not res.data.is_successful:
# Broker rejected the order
retcode = res.data.retcode
if retcode == constants.TRADE_RETCODE_NO_MONEY:
print("Insufficient funds")
elif retcode == constants.TRADE_RETCODE_MARKET_CLOSED:
print("Market is closed")
elif retcode == constants.TRADE_RETCODE_REQUOTE:
print(f"Requote — new price: {res.data.ask}")
else:
print(f"Order rejected: {res.data.comment} (retcode {retcode})")
else:
print(f"Order filled: deal={res.data.deal}, order={res.data.order}")
Two-level check for trade operations¶
order_send() and order_check() have two layers of success:
Two-level validation
res.success— did the SDK call succeed (connection, parsing, etc.)?res.data.is_successful— did the broker accept the trade?
res = mt5.order_send(request)
if res.success and res.data.is_successful:
print("Trade accepted")
elif res.success and not res.data.is_successful:
print(f"Trade rejected by broker: {res.data.comment}")
else:
print(f"SDK error: {res.error_message}")
Exception classes¶
Exceptions are reserved for unrecoverable situations that cannot return a Result (e.g. during object construction). You will not encounter these in normal usage.
| Exception | When raised |
|---|---|
MT5Error |
Base class for all SDK exceptions |
MT5ConnectionError |
Unrecoverable connection lifecycle failure |
MT5ExecutionError |
Unrecoverable data retrieval or trade execution failure |