Error Handling Guide#
Proper error handling is essential for building robust OUCH 5.0 applications.
Checking Send Results#
Always check the return value from send_message():
std::error_code ec = session.send_message(&order);
if (ec) {
std::cerr << "Failed to send order: " << ec.message() << std::endl;
// Handle error appropriately
}
Possible Errors#
send_message() can return the following errors:
Improper Session State (
Error::ImproperSessionState)Session is not in
EstablishedstateHandling: Check session state, wait for connection, or reconnect
Connection Lost (system error codes)
TCP connection broken or closed
Network errors (ECONNRESET, EPIPE, etc.)
Handling: Monitor state changes, implement reconnection logic
Success (empty error_code)
Message was sent successfully or queued for sending
If send queue is full, message is automatically queued and will be sent asynchronously
Error Handling Recommendations#
std::error_code ec = session.send_message(&order);
if (ec) {
// Check session state first
if (session.get_state() != Session::State::Established) {
std::cerr << "Cannot send: session not established" << std::endl;
// Wait for connection or trigger reconnection
return;
}
// Check for connection errors
if (ec.category() == std::system_category()) {
std::cerr << "Connection error: " << ec.message() << std::endl;
// Connection may be lost - monitor state changes for Disconnected
// Implement reconnection logic
} else {
std::cerr << "Send error: " << ec.message() << " (code: " << ec.value() << ")" << std::endl;
}
// For production: log error, potentially retry after reconnection
// If buffering is enabled, message will be retransmitted automatically
}
Note: If message buffering is enabled, messages with UserRefNum are automatically buffered. Even if send_message() returns an error, the message may have been buffered and will be retransmitted after reconnection.
Handling Rejected Orders#
Orders can be rejected for various reasons. Always handle rejections:
void on_rejected(Session* s, std::uint64_t seq, const Rejected* msg) {
UInt32 user_ref_num = msg->get_user_ref_num();
RejectReason reason = msg->get_reason();
std::cerr << "Order rejected: UserRefNum=" << user_ref_num
<< " Reason=" << static_cast<int>(reason) << std::endl;
// Handle rejection based on reason
switch (reason) {
case RejectReason::InvalidPrice:
// Retry with corrected price
break;
case RejectReason::InvalidQuantity:
// Retry with corrected quantity
break;
case RejectReason::DuplicateUserRefNum:
// Generate new UserRefNum and retry
break;
default:
// Handle other rejections
break;
}
}
Handling Cancel Rejects#
Cancel requests can be rejected:
void on_cancel_reject(Session* s, std::uint64_t seq, const CancelReject* msg) {
UInt32 user_ref_num = msg->get_user_ref_num();
std::cerr << "Cancel rejected for UserRefNum: " << user_ref_num << std::endl;
// Order remains active
}
Connection Errors#
Handle connection state changes:
void on_state_change(Session* s, Session::State old_state, Session::State new_state) {
if (new_state == Session::State::Disconnected) {
std::cerr << "Connection lost" << std::endl;
// Implement reconnection logic
} else if (new_state == Session::State::Established) {
std::cout << "Connection established" << std::endl;
// Recover UserRefNum after reconnection
recover_user_refnum(s);
}
}
Login Errors#
Handle login failures:
session.async_login("", 0, std::chrono::milliseconds(2000),
[](std::uint64_t seq, const std::error_code& ec) {
if (ec) {
std::cerr << "Login failed: " << ec.message() << std::endl;
// Retry login or handle error
} else {
std::cout << "Logged in successfully with sequence: " << seq << std::endl;
}
});
Exception Handling in Handlers#
Always wrap handler code in try-catch blocks:
void on_order_accepted(Session* s, std::uint64_t seq, const OrderAccepted* msg) {
try {
// Process order acceptance
process_order_accepted(msg);
} catch (const std::exception& e) {
std::cerr << "Error processing OrderAccepted: " << e.what() << std::endl;
// Log error, but don't crash
}
}
Reconnection Strategy#
Implement a reconnection strategy:
void reconnect(Session* session) {
// Wait before reconnecting
std::this_thread::sleep_for(std::chrono::seconds(1));
session->async_login("", 0, std::chrono::milliseconds(2000),
[session](std::uint64_t seq, const std::error_code& ec) {
if (!ec) {
// Recover UserRefNum
recover_user_refnum(session);
} else {
// Retry reconnection
reconnect(session);
}
});
}
UserRefNum Recovery#
Always recover UserRefNum after reconnection:
void recover_user_refnum(Session* session) {
AccountQueryRequest query;
session->send_message(&query);
// Handle in AccountQueryResponse handler
// void on_account_query_response(...) {
// UInt32 next_user_ref_num = msg->get_next_user_ref_num();
// session->get_user_refnum_generator().reset(next_user_ref_num);
// }
}
Error Logging#
Implement comprehensive error logging:
void log_error(const std::string& context, const std::error_code& ec) {
std::cerr << "[" << context << "] Error: " << ec.message()
<< " (code: " << ec.value() << ")" << std::endl;
}
void log_rejection(UInt32 user_ref_num, RejectReason reason) {
std::cerr << "Order rejected - UserRefNum: " << user_ref_num
<< ", Reason: " << static_cast<int>(reason) << std::endl;
}
Best Practices#
Always check return values from
send_message()- handle errors immediatelyMonitor state changes - implement state change handlers to detect disconnections
Enable message buffering for production - messages will be automatically retransmitted after reconnection
Handle all message types you care about, including error messages (Rejected, CancelReject)
Implement reconnection logic for production applications
Recover UserRefNum after every reconnection using
AccountQueryRequestWrap handlers in try-catch to prevent crashes from exceptions
Log errors appropriately for debugging - include error codes and context
Handle rejections gracefully - don’t assume orders will always be accepted
Don’t retry immediately on send errors - wait for reconnection if connection is lost