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:

  1. Improper Session State (Error::ImproperSessionState)

    • Session is not in Established state

    • Handling: Check session state, wait for connection, or reconnect

  2. Connection Lost (system error codes)

    • TCP connection broken or closed

    • Network errors (ECONNRESET, EPIPE, etc.)

    • Handling: Monitor state changes, implement reconnection logic

  3. 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#

  1. Always check return values from send_message() - handle errors immediately

  2. Monitor state changes - implement state change handlers to detect disconnections

  3. Enable message buffering for production - messages will be automatically retransmitted after reconnection

  4. Handle all message types you care about, including error messages (Rejected, CancelReject)

  5. Implement reconnection logic for production applications

  6. Recover UserRefNum after every reconnection using AccountQueryRequest

  7. Wrap handlers in try-catch to prevent crashes from exceptions

  8. Log errors appropriately for debugging - include error codes and context

  9. Handle rejections gracefully - don’t assume orders will always be accepted

  10. Don’t retry immediately on send errors - wait for reconnection if connection is lost