nautechsystems/nautilus_trader · error · anyhow::Error
Invalid Hyperliquid outcome symbol '{symbol}': encoding must
Error message
Invalid Hyperliquid outcome symbol '{symbol}': encoding must not be empty What it means
parse_outcome_symbol_encoding decodes the numeric encoding of an outcome symbol (#<encoding> or +<encoding>, where encoding = 10*outcome + side as u32). An empty encoding after the prefix is rejected with this error.
Source
Thrown at crates/adapters/hyperliquid/src/common/parse.rs:280
HyperliquidAssetId::from_outcome_encoding(encoding).with_context(|| {
format!(
"Invalid Hyperliquid outcome symbol '{symbol}': encoding must fit u32 and end with side digit 0 or 1"
)
})
}
fn parse_outcome_symbol_encoding(symbol: &str) -> anyhow::Result<u32> {
let encoding = symbol
.strip_prefix('#')
.or_else(|| symbol.strip_prefix('+'))
.with_context(|| {
format!(
"Invalid Hyperliquid outcome symbol '{symbol}': expected #<encoding> or +<encoding>"
)
})?;
if encoding.is_empty() {
anyhow::bail!("Invalid Hyperliquid outcome symbol '{symbol}': encoding must not be empty");
}
if !encoding.bytes().all(|b| b.is_ascii_digit()) {
anyhow::bail!("Invalid Hyperliquid outcome symbol '{symbol}': encoding must be numeric");
}
encoding
.parse::<u32>()
.with_context(|| format!("Invalid Hyperliquid outcome symbol '{symbol}'"))
}
/// Suffix shared by every Nautilus outcome symbol, mirroring `-PERP` / `-SPOT`.
pub const OUTCOME_SYMBOL_SUFFIX: &str = "-OUTCOME";
/// Yes-side label on Nautilus outcome symbols.
pub const OUTCOME_SIDE_YES: &str = "YES";
/// No-side label on Nautilus outcome symbols.
pub const OUTCOME_SIDE_NO: &str = "NO";
View on GitHub (pinned to 18893faf8b)
Solutions
- Supply the full outcome symbol including the numeric encoding, e.g. "#42" or "+11".
- Check upstream data/config for truncation of the symbol string.
- Validate non-empty digits after the prefix before invoking the parser.
Example fix
// before
parse_outcome_symbol("#")?; // empty encoding
// after
parse_outcome_symbol("#42")?; // outcome 4, side 2 Defensive patterns
Strategy: validation
Validate before calling
fn valid_outcome_symbol(s: &str) -> bool {
let digits = s.strip_prefix('#').or_else(|| s.strip_prefix('+'));
matches!(digits, Some(d) if !d.is_empty() && d.bytes().all(|b| b.is_ascii_digit()))
} Try / catch
match parse_outcome_symbol(sym) {
Ok(parsed) => use_outcome(parsed),
Err(e) => anyhow::bail!("malformed outcome symbol: {e}"),
} Prevention
- Validate the #/+ prefix plus non-empty digits before parsing.
- Guard against upstream truncation of symbol strings.
- Keep outcome symbols as typed values end-to-end, never re-sliced strings.
When it happens
Trigger: Calling parse_outcome_symbol with "#" or "+" (prefix present but no digits), which yields an empty encoding string.
Common situations: Truncated symbols from an upstream feed or config; string slicing that drops the digits; user-entered outcome IDs missing the number.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- Invalid Hyperliquid bar interval: {s}
- Invalid Hyperliquid symbol format: {symbol}
- Failed to create quantity from filled_sz: {e}
- Failed to create price from limit_px: {e}
- Failed to create trigger price: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/b89338ec9cca1118.
Report an issue: GitHub.