nautechsystems/nautilus_trader · error

Coinbase fill has unknown order side

Error message

Coinbase fill has unknown order side

What it means

Coinbase fill reports carry an order side that is mapped to Nautilus OrderSide. If Coinbase reports the side as Unknown (missing/unexpected value in the fill payload), parse_order_side refuses to guess and returns this error, since a fill without a valid side cannot be applied to a position.

Source

Thrown at crates/adapters/coinbase/src/http/parse.rs:501

/// Converts a Coinbase order side to the Nautilus [`Option<OrderSide>`].
pub fn parse_order_side_optional(side: &CoinbaseOrderSide) -> Option<OrderSide> {
    match side {
        CoinbaseOrderSide::Buy => Some(OrderSide::Buy),
        CoinbaseOrderSide::Sell => Some(OrderSide::Sell),
        CoinbaseOrderSide::Unknown => None,
    }
}

/// Converts a Coinbase order side to a Nautilus [`OrderSide`].
///
/// # Errors
///
/// Returns an error when Coinbase supplies an unknown side.
pub fn parse_order_side(side: &CoinbaseOrderSide) -> anyhow::Result<OrderSide> {
    match side {
        CoinbaseOrderSide::Buy => Ok(OrderSide::Buy),
        CoinbaseOrderSide::Sell => Ok(OrderSide::Sell),
        CoinbaseOrderSide::Unknown => anyhow::bail!("Coinbase fill has unknown order side"),
    }
}

/// Converts a Coinbase order status to the Nautilus [`OrderStatus`].
///
/// `Pending` and `Queued` are transient pre-`Open` states the venue passes
/// through after acknowledging the order. They are mapped to `Accepted`
/// (rather than `Submitted`) so user-channel updates that race the REST
/// `OrderAccepted` event do not appear as a backwards transition to the
/// reconciler. `Open` also maps to `Accepted` because Nautilus differentiates
/// the initial accept event from later partial-fill states; callers should
/// promote the status to `PartiallyFilled` / `Filled` based on `filled_qty`.
pub fn parse_order_status(status: CoinbaseOrderStatus) -> OrderStatus {
    match status {
        CoinbaseOrderStatus::Pending | CoinbaseOrderStatus::Queued | CoinbaseOrderStatus::Open => {
            OrderStatus::Accepted
        }
        CoinbaseOrderStatus::Filled => OrderStatus::Filled,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Inspect the raw fill payload from Coinbase to see what side value was returned
  2. Update the CoinbaseOrderSide deserializer if Coinbase changed its side representation
  3. Drop or quarantine fills with Unknown side instead of feeding them into position accounting

Example fix

// before
let side = parse_order_side(&fill.side)?;
// after
let Ok(side) = parse_order_side(&fill.side) else { log::warn!("skipping fill with unknown side"); continue; };
Defensive patterns

Strategy: try-catch

Validate before calling

if fill.side == CoinbaseOrderSide::Unknown { return Err("fill side missing".into()); }

Type guard

fn known_side(s: &CoinbaseOrderSide) -> Option<OrderSide> {
    parse_order_side(s).ok()
}

Try / catch

let side = match parse_order_side(&fill.side) {
    Ok(s) => s,
    Err(e) => { log::warn!("drop fill {}: {e}", fill.fill_id); continue; }
};

Prevention

When it happens

Trigger: parse_fill_report processing a Coinbase fill JSON whose side field is absent or an unrecognized value, deserializing to CoinbaseOrderSide::Unknown.

Common situations: Coinbase API schema changes or partial fill records with missing side; proxy/CDN mangling responses; processing historical fills from an API version with different side casing.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/ff631039338ced07. Report an issue: GitHub.