{"record":{"id":"098eef5004b1136b","repo":"nautechsystems/nautilus_trader","slug":"invalid-hyperliquid-outcome-symbol-symbol-enc-098eef","errorCode":null,"errorMessage":"Invalid Hyperliquid outcome symbol '{symbol}': encoding must be numeric","messagePattern":"Invalid Hyperliquid outcome symbol '(.+?)': encoding must be numeric","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/hyperliquid/src/common/parse.rs","lineNumber":284,"sourceCode":"    })\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\n/// Parses a Nautilus outcome instrument symbol of the form\n/// `{outcome_index}-{YES|NO}-OUTCOME` into `(outcome_index, side)` where side\n/// is `0` for Yes and `1` for No.\n///","sourceCodeStart":266,"sourceCodeEnd":302,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/hyperliquid/src/common/parse.rs#L266-L302","documentation":"parse_outcome_symbol_encoding validates the encoding portion of a Hyperliquid outcome symbol (a prediction-market instrument like 'BTC-up-or-down-...' with a numeric encoding). The library throws this because outcome symbol encodings must be plain ASCII digits parseable as u32; a non-numeric encoding cannot be mapped to Hyperliquid's internal outcome index.","triggerScenarios":"Calling parse_outcome_symbol (directly or via instrument/symbol parsing when subscribing or requesting data for a Hyperliquid outcome market) with a symbol whose encoding segment contains letters, dashes, spaces, or any non-digit character, e.g. '...-12x' or an empty/malformed segment that passed the earlier empty check.","commonSituations":"Hand-constructed outcome symbol strings, symbols copied from a different exchange's format, stale cached instrument IDs after a venue listing change, or programmatic symbol building that interpolates a non-numeric suffix.","solutions":["Inspect the symbol string and fix the encoding segment so it contains only digits 0-9 (e.g. '...-42').","Obtain outcome symbols from the adapter's parsed instrument definitions rather than constructing them manually.","Log the raw symbol at parse time and compare against the venue's outcome metadata to find the malformed segment.","If the venue changed its outcome symbol scheme, refresh instruments and update stored symbol references."],"exampleFix":"// before\nlet symbol = \"BTC-up-or-down-1700000000-ab\"; // non-numeric encoding\nlet outcome = parse_outcome_symbol(symbol)?;\n// after\nlet symbol = \"BTC-up-or-down-1700000000-3\"; // numeric encoding segment\nlet outcome = parse_outcome_symbol(symbol)?;","handlingStrategy":"validation","validationCode":"fn is_valid_outcome_encoding(encoding: &str) -> bool {\n    !encoding.is_empty() && encoding.bytes().all(|b| b.is_ascii_digit()) && encoding.parse::<u32>().is_ok()\n}\n// call before parse_outcome_symbol\nif !is_valid_outcome_encoding(&encoding) { /* fix symbol or skip */ }","typeGuard":"fn valid_outcome_symbol(symbol: &str) -> Option<&str> {\n    let enc = symbol.rsplit('-').next()?;\n    (!enc.is_empty() && enc.bytes().all(|b| b.is_ascii_digit())).then_some(symbol)\n}","tryCatchPattern":null,"preventionTips":["Source outcome symbols from the adapter's parsed instruments, not hand-built strings","Validate symbol segments contain only digits before use","Log raw symbols when parsing fails to catch format drift early"],"tags":["hyperliquid","symbol-parsing","validation","rust"],"backgroundTag":"invalid-argument-format","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}