{"record":{"id":"b89338ec9cca1118","repo":"nautechsystems/nautilus_trader","slug":"invalid-hyperliquid-outcome-symbol-symbol-enc","errorCode":null,"errorMessage":"Invalid Hyperliquid outcome symbol '{symbol}': encoding must not be empty","messagePattern":"Invalid Hyperliquid outcome symbol '(.+?)': encoding must not be empty","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/hyperliquid/src/common/parse.rs","lineNumber":280,"sourceCode":"    HyperliquidAssetId::from_outcome_encoding(encoding).with_context(|| {\n        format!(\n            \"Invalid Hyperliquid outcome symbol '{symbol}': encoding must fit u32 and end with side digit 0 or 1\"\n        )\n    })\n}\n\nfn parse_outcome_symbol_encoding(symbol: &str) -> anyhow::Result<u32> {\n    let encoding = symbol\n        .strip_prefix('#')\n        .or_else(|| symbol.strip_prefix('+'))\n        .with_context(|| {\n            format!(\n                \"Invalid Hyperliquid outcome symbol '{symbol}': expected #<encoding> or +<encoding>\"\n            )\n        })?;\n\n    if encoding.is_empty() {\n        anyhow::bail!(\"Invalid Hyperliquid outcome symbol '{symbol}': encoding must not be empty\");\n    }\n\n    if !encoding.bytes().all(|b| b.is_ascii_digit()) {\n        anyhow::bail!(\"Invalid Hyperliquid outcome symbol '{symbol}': encoding must be numeric\");\n    }\n\n    encoding\n        .parse::<u32>()\n        .with_context(|| format!(\"Invalid Hyperliquid outcome symbol '{symbol}'\"))\n}\n\n/// Suffix shared by every Nautilus outcome symbol, mirroring `-PERP` / `-SPOT`.\npub const OUTCOME_SYMBOL_SUFFIX: &str = \"-OUTCOME\";\n/// Yes-side label on Nautilus outcome symbols.\npub const OUTCOME_SIDE_YES: &str = \"YES\";\n/// No-side label on Nautilus outcome symbols.\npub const OUTCOME_SIDE_NO: &str = \"NO\";\n","sourceCodeStart":262,"sourceCodeEnd":298,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/hyperliquid/src/common/parse.rs#L262-L298","documentation":"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.","triggerScenarios":"Calling parse_outcome_symbol with \"#\" or \"+\" (prefix present but no digits), which yields an empty encoding string.","commonSituations":"Truncated symbols from an upstream feed or config; string slicing that drops the digits; user-entered outcome IDs missing the number.","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."],"exampleFix":"// before\nparse_outcome_symbol(\"#\")?; // empty encoding\n// after\nparse_outcome_symbol(\"#42\")?; // outcome 4, side 2","handlingStrategy":"validation","validationCode":"fn valid_outcome_symbol(s: &str) -> bool {\n    let digits = s.strip_prefix('#').or_else(|| s.strip_prefix('+'));\n    matches!(digits, Some(d) if !d.is_empty() && d.bytes().all(|b| b.is_ascii_digit()))\n}","typeGuard":null,"tryCatchPattern":"match parse_outcome_symbol(sym) {\n    Ok(parsed) => use_outcome(parsed),\n    Err(e) => anyhow::bail!(\"malformed outcome symbol: {e}\"),\n}","preventionTips":["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."],"tags":["hyperliquid","outcome-symbols","parsing","rust"],"backgroundTag":"invalid-identifier-format","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}