QuantConnect/Lean · error · AssertionError

OrderEvent Ticket was not set

Error message

OrderEvent Ticket was not set

What it means

Self-test assertion confirming that every OrderEvent delivered to the algorithm carries a back-reference to its OrderTicket. OrderEvent.Ticket is a JsonIgnore, runtime-only property (not serialized) that the transaction processor attaches when the event is dispatched to OnOrderEvent. The assertion fires when that Ticket reference is None/null on arrival.

Source

Thrown at Algorithm.Python/OrderTicketDemoAlgorithm.py:406

        order = self.transactions.get_order_by_id(order_event.order_id)
        self.log("{0}: {1}: {2}".format(self.time, order.type, order_event))

        if order_event.quantity == 0:
            raise AssertionError("OrderEvent quantity is Not expected to be 0, it should hold the current order Quantity")

        if order_event.quantity != order.quantity:
            raise AssertionError("OrderEvent quantity should hold the current order Quantity")

        if (type(order) is LimitOrder and order_event.limit_price == 0 or
            type(order) is StopLimitOrder and order_event.limit_price == 0):
            raise AssertionError("OrderEvent LimitPrice is Not expected to be 0 for LimitOrder and StopLimitOrder")

        if type(order) is StopMarketOrder and order_event.stop_price == 0:
            raise AssertionError("OrderEvent StopPrice is Not expected to be 0 for StopMarketOrder")

        # We can access the order ticket from the order event
        if order_event.ticket is None:
            raise AssertionError("OrderEvent Ticket was not set")
        if order_event.order_id != order_event.ticket.order_id:
            raise AssertionError("OrderEvent.ORDER_ID and order_event.ticket.order_id do not match")

    def check_pair_orders_for_fills(self, long_order, short_order):
        if long_order.status == OrderStatus.FILLED:
            self.log("{0}: Cancelling short order, long order is filled.".format(short_order.order_type))
            short_order.cancel("Long filled.")
            return True

        if short_order.status == OrderStatus.FILLED:
            self.log("{0}: Cancelling long order, short order is filled.".format(long_order.order_type))
            long_order.cancel("Short filled")
            return True

        return False


    def time_is(self, day, hour, minute):

View on GitHub (pinned to d2c3659f87)

Solutions

  1. As an engine regression: ensure the transaction manager sets OrderEvent.Ticket from the OrderTicket before invoking OnOrderEvent.
  2. If you replay/deserialize events: re-attach the ticket via self.transactions.get_order_ticket(order_event.order_id) (or the equivalent order-ticket lookup) before processing.
  3. In your own handler: guard against None and resolve the ticket by order id rather than asserting.
  4. Avoid constructing OrderEvent instances by hand for dispatch; route through the transaction processor so the ticket is attached.

Example fix

# before
if order_event.ticket is None:
    raise AssertionError("OrderEvent Ticket was not set")

# after (resolve the ticket when the runtime reference is missing, e.g. on replay)
ticket = order_event.ticket or self.transactions.get_order_ticket(order_event.order_id)
if ticket is None:
    raise AssertionError(f"No OrderTicket found for order id {order_event.order_id}")
Defensive patterns

Strategy: validation

Validate before calling

# Resolve the ticket defensively; Ticket is runtime-only and missing on replay/deserialized events
ticket = order_event.ticket
if ticket is None:
    ticket = self.transactions.get_order_ticket(order_event.order_id)
if ticket is None:
    self.debug(f"No ticket for order {order_event.order_id}; cannot process event")
    return

Type guard

def get_event_ticket(algorithm, order_event):
    """Return the OrderTicket for an event, resolving it when the runtime ref is missing."""
    return order_event.ticket or algorithm.transactions.get_order_ticket(order_event.order_id)

Prevention

When it happens

Trigger: An OrderEvent reaches on_order_event with order_event.ticket is None. This happens when the event was constructed or deserialized on a path that bypasses the transaction handler's ticket-attachment step (e.g. a manually built event, a replay/deserialized packet, or an engine change that stopped setting Ticket before delivery). Because Ticket is [JsonIgnore], events restored from JSON will never carry it unless re-attached.

Common situations: Lean contributors hit this after refactoring the order-event dispatch in the algorithm transaction manager. Users hit it when they synthesize or replay OrderEvent objects (e.g. from a stored result packet) and pass them straight to a handler. It also appears after version upgrades if the dispatch contract for attaching the ticket changed.

Related errors


AI-assisted analysis of QuantConnect/Lean@d2c3659f87 (2026-08-13). Data as JSON: /api/errors/b1feec0ebbf46ce0. Report an issue: GitHub.