{"record":{"id":"1fde803652b65c0c","repo":"QuantConnect/Lean","slug":"expected-order-ticket-in-order-event-to-not-be-nul","errorCode":null,"errorMessage":"Expected order ticket in order event to not be null","messagePattern":"Expected order ticket in order event to not be null","errorType":"exception","errorClass":"AssertionError","httpStatus":null,"severity":"error","filePath":"Algorithm.Python/OrderTicketAssignmentDemoAlgorithm.py","lineNumber":44,"sourceCode":"\n        self._symbol = self.add_equity(\"SPY\").symbol\n\n        self.trade_count = 0\n        self.consolidate(self._symbol, timedelta(hours=1), self.hour_consolidator)\n\n    def hour_consolidator(self, bar: TradeBar):\n        # Reset self.ticket to None on each new bar\n        self.ticket = None\n        self.ticket = self.market_order(self._symbol, 1, asynchronous=True)\n        self.debug(f\"{self.time}: Buy: Price {bar.price}, order_id: {self.ticket.order_id}\")\n        self.trade_count += 1\n\n    def on_order_event(self, order_event: OrderEvent):\n        # We cannot access self.ticket directly because it is assigned asynchronously:\n        # this order event could be triggered before self.ticket is assigned.\n        ticket = order_event.ticket\n        if ticket is None:\n            raise AssertionError(\"Expected order ticket in order event to not be null\")\n        if order_event.status == OrderStatus.SUBMITTED and self.ticket is not None:\n            raise AssertionError(\"Field self.ticket not expected no be assigned on the first order event\")\n\n        self.debug(ticket.to_string())\n\n    def on_end_of_algorithm(self):\n        # Just checking that orders were placed\n        if not self.portfolio.invested or self.trade_count != self.transactions.orders_count:\n            raise AssertionError(f\"Expected the portfolio to have holdings and to have {self.trade_count} trades, but had {self.transactions.orders_count}\")\n","sourceCodeStart":26,"sourceCodeEnd":54,"githubUrl":"https://github.com/QuantConnect/Lean/blob/d2c3659f877bfc2b5d9dc0fc89a9c7566f45e892/Algorithm.Python/OrderTicketAssignmentDemoAlgorithm.py#L26-L54","documentation":"OrderTicketAssignmentDemoAlgorithm places asynchronous market orders and demonstrates that you cannot rely on the return value being assigned to self.ticket before order events fire. In on_order_event it reads order_event.ticket (the ticket the engine attaches to the event) and asserts it is not None. A None ticket means the engine failed to associate the OrderTicket with the event for that order — a regression in Lean's order-event/ticket wiring.","triggerScenarios":"An OrderEvent arrives in on_order_event whose .ticket property is None. This happens if the engine does not set the ticket reference on the event, if the order was generated internally (e.g. margin call) without a ticket, or after a refactor of OrderEvent.Ticket population.","commonSituations":"A Lean change decoupling OrderEvent from its OrderTicket; internal/automatic orders (liquidations, margin calls, option assignment/exercise) that legitimately lack a user ticket; a brokerage event path that bypasses ticket assignment.","solutions":["Confirm the order was user-submitted (via market_order/asynchronous=True); for internal orders, guard with `if order_event.ticket is None: return`.","If you modified Lean, ensure OrderEvent.Ticket is populated for every user order event path.","Use order_event.order_id to look up the ticket via self.transactions.get_order_by_id(...) as a fallback.","Filter out non-user order events (e.g. check order_event.ticket is not None only for OrderStatus you expect)."],"exampleFix":"# before\nticket = order_event.ticket\nif ticket is None:\n    raise AssertionError('Expected order ticket in order event to not be null')\n# after: tolerate internal orders, look up by id\nticket = order_event.ticket or self.transactions.get_order_ticket(order_event.order_id)\nif ticket is None:\n    return  # internal/automatic order without a user ticket","handlingStrategy":"type-guard","validationCode":"# Prefer the event's ticket; fall back to lookup by order id\nticket = order_event.ticket or self.transactions.get_order_ticket(order_event.order_id)\nif ticket is None:\n    return  # internal/automatic order without a user ticket","typeGuard":"def has_ticket(order_event) -> bool:\n    return order_event.ticket is not None","tryCatchPattern":null,"preventionTips":["Rely on order_event.ticket (or lookup by order_id) instead of a shared self.ticket variable.","Guard for internal orders (margin calls, assignments) that may lack a user ticket.","After Lean changes, verify OrderEvent.Ticket is populated for user orders.","Tolerate None for non-user event types."],"tags":["orders","order-ticket","order-event","asynchronous","quantconnect","demo"],"backgroundTag":null,"analyzedSha":"d2c3659f877bfc2b5d9dc0fc89a9c7566f45e892","analyzedAt":"2026-08-13T13:52:21.013Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}