{"record":{"id":"0bd7b797538b6693","repo":"nautechsystems/nautilus_trader","slug":"metadata-user-must-not-contain-surrounding-whit","errorCode":null,"errorMessage":"metadata['user'] must not contain surrounding whitespace","messagePattern":"metadata\\['user'\\] must not contain surrounding whitespace","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/hyperliquid/src/data.rs","lineNumber":367,"sourceCode":"        };\n\n        let instrument_id = InstrumentId::from_str(raw_instrument_id)\n            .with_context(|| format!(\"invalid instrument_id metadata `{raw_instrument_id}`\"))?;\n\n        Ok(Some(instrument_id))\n    }\n\n    fn custom_user(data_type: &DataType) -> anyhow::Result<Option<String>> {\n        let Some(user) = data_type\n            .metadata()\n            .and_then(|m| m.get(\"user\"))\n            .and_then(|v| v.as_str())\n            .filter(|value| !value.is_empty())\n        else {\n            return Ok(None);\n        };\n\n        anyhow::ensure!(\n            user == user.trim(),\n            \"metadata['user'] must not contain surrounding whitespace\",\n        );\n\n        Ok(Some(user.to_string()))\n    }\n\n    async fn bootstrap_instruments(&self) -> anyhow::Result<Vec<InstrumentAny>> {\n        let instruments = self\n            .http_client\n            .request_instruments()\n            .await\n            .context(\"failed to fetch instruments during bootstrap\")?;\n\n        self.instruments.rcu(|m| {\n            for instrument in &instruments {\n                m.insert(instrument.id(), instrument.clone());\n            }","sourceCodeStart":349,"sourceCodeEnd":385,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/hyperliquid/src/data.rs#L349-L385","documentation":"The `custom_user` helper extracts `metadata['user']` from instrument definition params and rejects values with leading or trailing whitespace via `anyhow::ensure!`. The adapter treats whitespace-padded user strings as invalid configuration rather than silently trimming, because the value is used as an exact identifier. It prevents subtly wrong user attribution in Hyperliquid instrument metadata.","triggerScenarios":"Providing an instrument definition/config parameter `metadata.user` (or equivalent custom user field) such as \" 0xabc... \" or \"my-user\\n\" when building the data client or parsing instrument definitions.","commonSituations":"Copying a user/address from a document or terminal with trailing spaces; YAML/JSON config with accidentally indented quoted values; templated config emitting newlines.","solutions":["Trim the value before passing it: pass `user.trim()` in your config or builder call.","Fix the source config file so the quoted string has no padding.","If generated programmatically, sanitize at the point of construction."],"exampleFix":"// before\nlet user = Some(\" 0xabc123 \".to_string());\n// after\nlet user = Some(\" 0xabc123 \".trim().to_string());","handlingStrategy":"validation","validationCode":"fn validate_custom_user(user: &str) -> Result<(), String> {\n    if user.trim() != user {\n        return Err(format!(\"metadata['user'] has surrounding whitespace: {user:?}\"));\n    }\n    if user.is_empty() {\n        return Err(\"metadata['user'] is empty\".into());\n    }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always `.trim()` user-supplied identifiers before placing them in config.","Lint config files for accidental indentation inside quoted strings.","Prefer programmatic construction over copy-paste for identifiers."],"tags":["validation","config","whitespace","hyperliquid"],"backgroundTag":"invalid-config-value","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"}