Skip to main content

Order statuses and lifecycle semantics

Order status is a stream of observations, not a guaranteed linear state machine. IBKR can send duplicate statuses, omit intermediate statuses, and deliver an execution without a preceding Submitted callback. Correct systems consume openOrder, orderStatus, execDetails, and error together.

This page uses IBKR's current Campus TWS API status table as the primary source. It also identifies statuses documented on IBKR's official legacy order-submission page and shows how ib_async 2.1.0 classifies them.

Current IBKR Campus statuses

Inactive

IBKR Campus describes this as an order being created but not yet activated or transmitted. It is not safe to interpret the word as one single failure reason.

IBKR's older official TWS documentation also associates Inactive with invalid/rejected orders, short-sale locate holds, manual orders entered while an exchange is closed, and orders blocked by TWS precautions. Therefore:

  • inspect the matching error callback, especially rejection code 201;
  • inspect OrderState.warningText from openOrder;
  • inspect whyHeld, account permissions, short availability, and TWS precaution dialogs;
  • do not automatically resubmit—the original order might be awaiting operator action or a locate;
  • persist the full status/error/warning combination, not just the string Inactive.

ib_async 2.1.0 includes Inactive in OrderStatus.DoneStates, so trade.isDone() returns true. That is a library cleanup classification, not proof that the business intent permanently failed or that resubmission is safe.

PendingSubmit

The order was transmitted from the client/TWS side, but acceptance by the destination has not been confirmed. The exchange being closed is a common reason, but not the only one.

The order is not yet confirmed working at its destination. Do not repeatedly call placeOrder to “make sure”; wait for callbacks or reconcile by identifiers. A modification can fail with warning 2102 while the original order is still being processed.

ib_async classifies this as active and waiting: isActive() == True, isWaiting() == True, isWorking() == False.

PreSubmitted

The order has been accepted by the IBKR system for a simulated order, or accepted by an exchange for a native order, but has not yet been elected/triggered. Typical examples include simulated stops and held child orders.

It is capable of becoming executable later. “Pre” does not mean rejected, and it does not mean there is no risk: the election condition can occur before your application receives another status callback.

ib_async classifies it as active and waiting, not working at a public destination.

Submitted

IBKR says the order has been accepted and is working at the destination. It may be unfilled, partially filled, or subject to a price cap.

Check the callback's filled, remaining, avgFillPrice, lastFillPrice, and mktCapPrice; the status string alone does not distinguish zero fill from partial fill. Submitted does not guarantee the next callback will be Filled or Cancelled, and it does not guarantee the order can still be modified before another execution.

ib_async classifies it as active and working: isActive() == True, isWorking() == True.

Filled

IBKR reports the order as completely filled. Treat execDetails records, deduplicated by execId, as the execution ledger. The final status fields are a useful aggregate but are not a substitute for fills and their later commission reports.

A market order can execute immediately without intermediate PendingSubmit or Submitted callbacks. Filled is terminal for the order, but executions can still be followed by commission data or, exceptionally, corrections/bust-related events.

ib_async includes Filled in DoneStates and emits trade.filledEvent when its wrapper observes a transition into this status.

PendingCancel

A cancel request was sent, but the destination has not confirmed cancellation. The order is not cancelled yet and can still execute, including completely.

Do not release reserved quantity, submit a replacement, or mark the remaining quantity dead until the cancellation or executions are reconciled. Possible outcomes include Cancelled, Filled, another working status, or an error that the order was not cancellable.

In ib_async 2.1.0, PendingCancel is in neither ActiveStates nor DoneStates. Thus both trade.isActive() and trade.isDone() return false. Use the exact status plus live fill/cancel handling rather than interpreting those two booleans as an exhaustive partition.

PreCancelled

IBKR Campus says the cancellation request was accepted by the system, but is not currently being recognized because of a system, exchange, or other issue. Cancellation remains unconfirmed and an execution can still arrive.

Operationally, handle it like an unresolved cancellation with heightened monitoring. Reconcile the live order and do not submit a replacement based solely on PreCancelled.

ib_async 2.1.0 does not define a PreCancelled class constant or include it in ActiveStates, WaitingStates, WorkingStates, or DoneStates. The raw string can still arrive in OrderStatus.status; application code must handle it explicitly.

Cancelled

The remaining balance is confirmed cancelled by the system. This can follow your cancel request, but IBKR notes it can also occur unexpectedly when the destination rejects or cancels an order.

Cancelled does not mean “nothing traded.” Check filled and execDetails; a partially filled order can have only its remainder cancelled. Correlate error 202 and other warning/error text to learn why cancellation occurred.

ib_async includes Cancelled in DoneStates and emits trade.cancelledEvent when its wrapper observes a transition into this exact status.

WarnState

IBKR Campus defines this as an order with a specific warning, with basket orders as an example. The status string alone does not describe the warning or establish whether the order is executable.

Read OrderState.warningText, errorEvent, and the TWS/API logs. Resolve the warning according to the workflow; never auto-override all warnings.

ib_async 2.1.0 does not define or classify WarnState, so all four helpers—isActive, isWaiting, isWorking, and isDone—return false for the raw status.

Additional statuses in IBKR's official legacy TWS documentation

The official legacy order-submission page documents two API-side statuses not present in the current Campus table:

ApiPending

The API order has not yet been sent to the IBKR server, for example because TWS is still resolving the security definition. IBKR calls this uncommon. It is earlier than destination acknowledgement and must not be treated as working.

ib_async classifies it as active and waiting.

ApiCancelled

An API client cancelled the order after submission but before acknowledgement. This is distinct from Cancelled, which confirms cancellation of the remaining balance by the system/destination path.

ib_async classifies ApiCancelled as done. Persist the status and still reconcile executions because cancel/execute races must never be decided from a single callback.

ib_async statuses that are not defined by the current IBKR Campus table

ib_async 2.1.0 also declares ApiUpdate and ValidationError. They are library/protocol-compatibility values and are not described in IBKR Campus's current “Understanding Order Status Message” table.

  • ApiUpdate is classified by the library as active and working.
  • ValidationError is also classified as active and working because the library accounts for validation during submission or modification that may leave the original order live or subsequently return to Submitted.

Do not invent stronger server semantics for these values. Record the exact callback sequence and corresponding error/warning text. In particular, a validation error during modification does not prove that the original working order was cancelled.

Status classification matrix

The “IBKR meaning” columns below follow the official documentation. Helper results describe ib_async 2.1.0, not IBKR guarantees.

StatusConfirmed working at destination?Terminal confirmation?ib_async waitingworkingactivedone
ApiPendingNoNoYesNoYesNo
PendingSubmitNot yetNoYesNoYesNo
PreSubmittedHeld/not electedNoYesNoYesNo
SubmittedYesNoNoYesYesNo
PendingCancelPossiblyNoNoNoNoNo
PreCancelledPossiblyNoNoNoNoNo
FilledFinished executingYes for order quantityNoNoNoYes
ApiCancelledNo/never acknowledgedAPI cancellation terminalNoNoNoYes
CancelledNo remaining balanceYes for remaining balanceNoNoNoYes
InactiveContext-dependentLibrary treats as doneNoNoNoYes
WarnStateUnknown from status aloneNoNoNoNoNo
ApiUpdateLibrary treats as workingNoNoYesYesNo
ValidationErrorOriginal order may remain workingNoNoYesYesNo

orderStatus callback fields

orderStatus(
orderId, status, filled, remaining, avgFillPrice,
permId, parentId, lastFillPrice, clientId, whyHeld, mktCapPrice
)
FieldInterpretation and trap
orderIdAPI client-scoped order ID. It is not a global durable key.
statusOne of the strings above—or a future/less-common value. Preserve unknown strings.
filledCumulative quantity reported filled for the order. Use execDetails for the fill ledger.
remainingQuantity not yet filled; after cancellation this is commonly the cancelled balance, not quantity still working.
avgFillPriceAverage across reported executions; zero can mean no fills rather than a real price.
permIdIBKR permanent order identifier; prefer it for cross-session correlation once assigned.
parentIdParent's API order ID, relevant to bracket/attached structures.
lastFillPriceMost recent reported execution price, not the order's limit or current market.
clientIdClient to which the order is bound. Individual modification/cancellation normally requires that client.
whyHeldHold reason. IBKR documents locate for a short-sale locate. Empty does not prove unheld.
mktCapPriceCurrent IBKR price cap when capped; otherwise normally zero. It is not your submitted limit.

filled + remaining often reconstructs order size, but modifications, allocations, combo behavior, fractional quantities, and callback timing make it inappropriate as a universal immutable-original-quantity formula.

Callback guarantees and non-guarantees

IBKR explicitly warns that:

  • duplicate orderStatus messages are common because updates can originate at TWS, IBKR, or the exchange;
  • there is no guarantee of a callback for every intermediate state;
  • immediately executing market orders often skip status callbacks;
  • execDetails must be monitored alongside orderStatus;
  • mktCapPrice reports an applied price cap when present.

Callbacks should therefore be processed as idempotent facts:

def on_status(trade):
s = trade.orderStatus
persist_status_observation(
perm_id=s.permId,
client_id=s.clientId,
order_id=s.orderId,
status=s.status, # preserve unknown strings
filled=s.filled,
remaining=s.remaining,
why_held=s.whyHeld,
market_cap=s.mktCapPrice,
)

def on_fill(trade, fill):
upsert_execution(fill.execution.execId, fill)

ib.orderStatusEvent += on_status
ib.execDetailsEvent += on_fill
ib.errorEvent += persist_correlated_error

Deduplicate status observations by their complete payload or store them as an append-only event stream. Deduplicate fills by execId. Never submit a replacement merely because an expected status was skipped.

Typical paths—not promises

API-held: ApiPending -> PendingSubmit -> Submitted -> Filled
Simulated: PendingSubmit -> PreSubmitted -> Submitted -> Filled
Cancel: Submitted -> PendingCancel -> Cancelled
Cancel race: Submitted -> PendingCancel -> Filled
Partial cancel: Submitted (filled > 0) -> PendingCancel -> Cancelled
Rejected: PendingSubmit/Inactive -> Cancelled or Inactive + error callback

Real sequences can omit nodes, repeat nodes, move between held/working states, or contain statuses not shown. Build business decisions from the combination of executions, confirmed remaining-balance cancellation, current open-order reconciliation, and correlated errors—not from an assumed transition graph.

Completed-order status is separate

reqCompletedOrders returns OrderState.status plus completedStatus/completedTime data for orders no longer modifiable during the current-day scope. It is a recovery/history surface, not a replacement for live orderStatus handling. Open-order requests cannot retrieve fully filled or cancelled orders.

Official sources