nautechsystems/nautilus_trader · error · anyhow::Error

Invalid Hyperliquid symbol format: {symbol}

Error message

Invalid Hyperliquid symbol format: {symbol}

What it means

InstrumentId/symbol parsing for Hyperliquid classifies a symbol as perp, spot, or outcome by suffix (e.g. -SPOT or outcome encodings). A symbol matching none of the recognized forms bails with this error.

Source

Thrown at crates/adapters/hyperliquid/src/common/enums.rs:1046

    /// Extract product type from an instrument symbol.
    ///
    /// Accepts both Nautilus instrument symbols (`{BASE}-USD-PERP`,
    /// `{BASE}-{QUOTE}-SPOT`, `{N}-{YES|NO}-OUTCOME`) and venue wire coin
    /// names (`#<encoding>` / `+<encoding>` for HIP-4 outcomes). Callers in
    /// the adapter pass both forms.
    ///
    /// # Errors
    ///
    /// Returns error if symbol doesn't match any expected format.
    pub fn from_symbol(symbol: &str) -> anyhow::Result<Self> {
        if symbol.ends_with("-PERP") {
            Ok(Self::Perp)
        } else if symbol.ends_with("-SPOT") {
            Ok(Self::Spot)
        } else if symbol.ends_with(OUTCOME_SYMBOL_SUFFIX) || is_outcome_wire_symbol(symbol) {
            Ok(Self::Outcome)
        } else {
            anyhow::bail!("Invalid Hyperliquid symbol format: {symbol}")
        }
    }
}

// Outcomes use the `#<encoding>` spot-coin form or the `+<encoding>` token
// form, where the encoding is `10 * outcome + side` and must parse as `u32`.
fn is_outcome_wire_symbol(symbol: &str) -> bool {
    let Some(rest) = symbol
        .strip_prefix('#')
        .or_else(|| symbol.strip_prefix('+'))
    else {
        return false;
    };
    !rest.is_empty() && rest.parse::<u32>().is_ok()
}

/// Hyperliquid API environment.
#[derive(

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Format the symbol per Hyperliquid conventions (perp form, spot form with -SPOT suffix, or #/+ outcome encoding).
  2. Pre-validate the symbol suffix in your ingest/config layer before calling from_symbol.
  3. Convert external symbols explicitly (e.g. BTCUSDT -> BTC or BTC-SPOT) rather than passing them through.

Example fix

// before
let asset = HyperliquidAssetType::from_symbol("BTCUSDT")?;
// after
let asset = HyperliquidAssetType::from_symbol("BTC")?; // perp
// or HyperliquidAssetType::from_symbol("BTC-SPOT")?;
Defensive patterns

Strategy: validation

Validate before calling

fn looks_like_hl_symbol(s: &str) -> bool {
    s.ends_with("-SPOT") || s.starts_with('#') || s.starts_with('+') || !s.contains("USDT")
}

Try / catch

match HyperliquidAssetType::from_symbol(sym) {
    Ok(a) => use_asset(a),
    Err(e) => { log::error!("{e}"); /* convert symbol format and retry */ }
}

Prevention

When it happens

Trigger: Calling from_symbol with a symbol lacking a recognized suffix, e.g. plain "BTC" without the expected perp/spot/outcome form, or a malformed instrument ID string.

Common situations: Passing Binance-style symbols (BTCUSDT) into the Hyperliquid adapter; missing the @/spot or -SPOT suffix; using outcome tokens without the # or + prefix.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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