nautechsystems/nautilus_trader · error

Failed to derive order hash: {e}

Error message

Failed to derive order hash: {e}

What it means

expected_order_id computes the EIP-712/keccak order hash via order_hash(order, neg_risk) to derive the venue order ID. When hash computation fails (malformed order fields, invalid salt/expiration encoding, or signer/wallet construction problems), the underlying error is wrapped as "Failed to derive order hash: {e}".

Source

Thrown at crates/adapters/polymarket/src/execution/order_builder.rs:224

            anyhow::bail!(
                "Polymarket market BUY amount {} pUSD truncates to zero at {LOT_SIZE_SCALE} decimal places",
                amount.normalize(),
            );
        }

        let (maker_amount, taker_amount) =
            compute_market_maker_taker_amounts(price, amount, side, tick_decimals);
        self.build_and_sign(token_id, side, maker_amount, taker_amount, "0", neg_risk)
    }

    /// Computes the Polymarket order ID for a signed CLOB V2 order.
    pub fn expected_order_id(
        &self,
        order: &PolymarketOrder,
        neg_risk: bool,
    ) -> anyhow::Result<VenueOrderId> {
        let hash = order_hash(order, neg_risk)
            .map_err(|e| anyhow::anyhow!("Failed to derive order hash: {e}"))?;
        let order_id = format!("{hash:#x}");
        Ok(VenueOrderId::from(order_id.as_str()))
    }

    /// Validates a limit order before building, returning a denial reason if invalid.
    pub fn validate_limit_order(order: &OrderAny) -> Result<(), OrderDeniedReason> {
        if order.is_reduce_only() {
            return Err(validation_failed(
                "Reduce-only orders not supported on Polymarket",
            ));
        }

        if order.order_type() != OrderType::Limit {
            return Err(OrderDeniedReason::UnsupportedOrderType {
                order_type: order.order_type(),
            });
        }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Read the wrapped inner error ({e}) — it names the exact encoding or wallet failure to fix.
  2. Ensure the order was built by PolymarketOrderBuilder::build_* so all EIP-712 fields (salt, maker, signer, expiration, neg_risk) are populated.
  3. Verify the neg_risk flag matches the market being traded.

Example fix

// before
let id = builder.expected_order_id(&hand_built_order, false)?; // hand-built, missing fields
// after
let poly_order = builder.build_limit_order(&order, ...)?; // builder populates all hash inputs
let id = builder.expected_order_id(&poly_order, market_neg_risk)?;
Defensive patterns

Strategy: try-catch

Validate before calling

if order.maker.is_empty() || order.salt.is_empty() || order.signature.is_empty() { return Err("order missing EIP-712 fields".into()); }

Type guard

fn is_hashable(order: &PolymarketOrder) -> bool {
    !order.maker.is_empty() && !order.signer.is_empty() && !order.salt.is_empty()
}

Try / catch

match builder.expected_order_id(&order, neg_risk) {
    Ok(id) => id,
    Err(e) => { tracing::error!("order hash derivation failed: {e:#}"); return Err(e); }
}

Prevention

When it happens

Trigger: Calling expected_order_id with a PolymarketOrder whose fields cannot be encoded per the CLOB EIP-712 schema, or with an incorrect neg_risk flag relative to the market, or when the signing wallet/key material is invalid.

Common situations: Passing neg_risk=false for a neg-risk market (or vice versa); orders built outside the normal builder path with missing maker/salt fields; corrupted or misconfigured private key used by the signer.

Related errors


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