nautechsystems/nautilus_trader · error · anyhow::Error

Unsupported time in force for Hyperliquid: {tif:?}

Error message

Unsupported time in force for Hyperliquid: {tif:?}

What it means

Catch-all rejection in time_in_force_to_hyperliquid_tif for any (TimeInForce, is_post_only) combination not explicitly mapped. Only Gtc/Ioc/Fok with post_only=false and any TIF with post_only=true (mapped to ALO) are recognized; anything else (e.g. a newer TimeInForce variant like Day or Gtd, or an unexpected combination) fails with this debug-formatted message.

Source

Thrown at crates/adapters/hyperliquid/src/common/parse.rs:386

}

/// Converts a Nautilus `TimeInForce` to Hyperliquid TIF.
///
/// # Errors
///
/// Returns an error if the time in force is not supported.
pub fn time_in_force_to_hyperliquid_tif(
    tif: TimeInForce,
    is_post_only: bool,
) -> anyhow::Result<HyperliquidExchangeTif> {
    match (tif, is_post_only) {
        (_, true) => Ok(HyperliquidExchangeTif::Alo), // Always use ALO for post-only orders
        (TimeInForce::Gtc, false) => Ok(HyperliquidExchangeTif::Gtc),
        (TimeInForce::Ioc, false) => Ok(HyperliquidExchangeTif::Ioc),
        (TimeInForce::Fok, false) => {
            anyhow::bail!("FOK time in force is not supported by Hyperliquid")
        }
        _ => anyhow::bail!("Unsupported time in force for Hyperliquid: {tif:?}"),
    }
}

fn determine_tpsl_type(
    order_type: OrderType,
    order_side: OrderSide,
    trigger_price: Decimal,
    current_price: Option<Decimal>,
) -> HyperliquidExchangeTpSl {
    match order_type {
        // Stop orders are protective - always SL
        OrderType::StopMarket | OrderType::StopLimit => HyperliquidExchangeTpSl::Sl,

        // If Touched orders are profit-taking or entry orders - always TP
        OrderType::MarketIfTouched | OrderType::LimitIfTouched => HyperliquidExchangeTpSl::Tp,

        // For other trigger types, try to infer from price relationship if available
        _ => {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Set the order's time_in_force to Gtc or Ioc (the TIFs Hyperliquid supports with non-post-only orders).
  2. If you need Gtd/Day semantics, emulate them by scheduling cancellation client-side instead of relying on the exchange TIF.
  3. Check the adapter's supported TIF mapping and your crate versions; upgrade the hyperliquid adapter if it lacks a mapping for a core TIF variant you need.
  4. Verify is_post_only/TIF combination explicitly before calling submit or modify.

Example fix

// before
order.time_in_force = TimeInForce::Gtd; // unmapped variant
// after
order.time_in_force = TimeInForce::Gtc; // supported by Hyperliquid
Defensive patterns

Strategy: validation

Validate before calling

fn tif_is_mapped(tif: TimeInForce) -> bool {
    matches!(tif, TimeInForce::Gtc | TimeInForce::Ioc | TimeInForce::Fok)
}
assert!(tif_is_mapped(order.time_in_force()), "TIF not mapped for Hyperliquid");

Try / catch

if let Err(e) = adapter.submit_order(order).await {
    if e.to_string().starts_with("Unsupported time in force") {
        log::warn!("downgrading TIF to Gtc: {e}");
        // resubmit with TimeInForce::Gtc
    } else { return Err(e); }
}

Prevention

When it happens

Trigger: Passing a TimeInForce value outside {Gtc, Ioc, Fok} with post_only=false — commonly a newly added or venue-agnostic TIF variant such as TimeInForce::Gtd or ::Day — to any order submission or modification path that builds a Hyperliquid request.

Common situations: Strategy code written against a newer Nautilus core with TIF variants this adapter doesn't map; TIF set from config/templates without checking Hyperliquid support; version mismatch between nautilus-core and the hyperliquid adapter crate.

Related errors


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