What it means when an order doesn’t fill: what the hub declined before anything opened, what it refused after the user’s own transaction was out, what the upstream reports about a fill attempt, and what the chain itself says.

Declined before anything was opened

The full per-code rows are on Submit order (openFor) — Errors — not restated here.

Refused after the transaction was sent

On the proxy lanes (oif-wrap-native, oif-swap-token) the user’s own transaction opens the order in escrow before POST /order/openfor registers it, so a refusal there comes after the deposit: A new quote would mean a second deposit. The deposit completes on chain anyway — the solver still fills it — but the hub never records the order, so it is missing from GET /orders and the status routes (known issue). Follow it by the send transaction’s hash on GET /onchain/tx/{chainDomain}/{txHash}/order, then GET /onchain/{orderId}/fill, and withdraw after the escrow expires if it is not filled (Refunds). The open flow is the same: POST /order comes after the user sent executionTx, so none of its refusals (EIN0005 past expiresAt, EIN0006, EIN0007, EIN0010) is a reason to re-quote — the transaction already sent completes on chain anyway, unregistered.

The upstream’s failure report

failed and refunded are reachable from any non-terminal upstream state — a fill attempt can fail at any point in its life, not just at the start. failedAt names the upstream’s transaction stage at which the attempt failed, one of: Prepare, Fill, PostFill, PreClaim, Claim. The hub gives no meaning for each stage beyond its name — treat the list as an enum to display, not a set of causes to explain. failureReason is the upstream’s own text — display it, never parse it; the hub does not normalize it into a code. history[] lists the states the hub read on the way there, which is not always the full path: a state passed between two reads is absent. Once state reaches failed or refunded, stop polling — see Order lifecycle.

Reports the hub refuses

The upstream’s own report of an order’s state isn’t taken at face value:
  • rejectedUpstreamTransition: true — the latest report was backwards, unknown, or contradicted the accepted state, so the hub kept the previous one instead.
  • upstreamStale: true — the accepted state couldn’t be refreshed on the last attempt; the last good state is still served.
  • EIN0007 with no earlier state — the hub has nothing to fall back to when an upstream answer is refused this early.
Keep polling through any of these — none of them is a terminal answer on its own.

What the chain says

GET /orders’ chain.status can read EXPIRED (render it as a failure) or REFUNDED — both from the chain’s own facts rather than the upstream’s report. REFUNDED is terminal; EXPIRED is not: no fill can complete the order any more, but once its refund is observed the entry reads REFUNDED, so keep reading it. What happens to a proxy-lane deposit whose registration was refused: Refunds.

Terminal states

The full table of terminal states per view (upstream, hub projection, on-chain) is on Order lifecycle — terminal states per view. Recovery of an order that neither fills nor refunds is an operator process outside this API.

See also