{"record":{"id":"49f3c89ff6a9f432","repo":"nautechsystems/nautilus_trader","slug":"lighter-api-secret-must-be-a-40-byte-hex-private-k","errorCode":null,"errorMessage":"Lighter API secret must be a 40-byte hex private key","messagePattern":"Lighter API secret must be a 40-byte hex private key","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/lighter/src/common/credential.rs","lineNumber":327,"sourceCode":"        .trim()\n        .parse::<u8>()\n        .with_context(|| format!(\"{env_var} must be an API key index in 0..=254\"))?;\n    ensure_api_key_index(index)\n}\n\nfn ensure_api_key_index(value: u8) -> anyhow::Result<u8> {\n    anyhow::ensure!(value <= 254, \"Lighter API key index must be in 0..=254\");\n    Ok(value)\n}\n\nfn decode_private_key_hex(value: &str) -> anyhow::Result<Vec<u8>> {\n    let value = value.trim();\n    let hex = value\n        .strip_prefix(\"0x\")\n        .or_else(|| value.strip_prefix(\"0X\"))\n        .unwrap_or(value);\n    let bytes = hex::decode(hex).context(\"Lighter API secret must be valid hex\")?;\n    anyhow::ensure!(\n        bytes.len() == SCALAR_BYTES,\n        \"Lighter API secret must be a 40-byte hex private key\"\n    );\n    Ok(bytes)\n}\n\n#[cfg(test)]\nmod tests {\n    use rstest::rstest;\n\n    use super::*;\n\n    const PRIVATE_KEY_HEX: &str =\n        \"0b8e0f63c24d8baacd9d29ad4e9a4b73c4a8d2bb8b16dc4fa9d7c2e1d3a8b1f0e8d3a4c5b6e7f001\";\n\n    #[rstest]\n    fn test_credential_env_vars_mainnet() {\n        assert_eq!(","sourceCodeStart":309,"sourceCodeEnd":345,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/lighter/src/common/credential.rs#L309-L345","documentation":"The Lighter private key decoder requires the API secret to decode from hex into exactly 40 bytes (SCALAR_BYTES). The value may optionally have a 0x/0X prefix; after stripping, `hex::decode` must succeed (else a different 'valid hex' error) and the byte length must equal 40. This error means the hex decoded fine but was not 40 bytes.","triggerScenarios":"Setting the Lighter API secret env var to a hex string that decodes to fewer or more than 40 bytes — e.g. a truncated key, a 32-byte key from another system, or an ed25519 key of unexpected length.","commonSituations":"Copying an incomplete key (clipboard truncation); using an Ethereum 32-byte private key instead of the Lighter 40-byte API secret; including stray whitespace is handled by trim but extra characters change length.","solutions":["Verify the API secret is the full 40-byte (80 hex character) private key from your Lighter account.","Re-copy the key carefully, without truncation, optionally with a 0x prefix.","Check `bytes.len()` from the hex string: 80 hex chars (excluding 0x) is required.","If you have a different-length key, obtain the correct Lighter API secret rather than padding the key."],"exampleFix":"// before\nLIGHTER_API_SECRET=abc123  // decodes to 3 bytes\n// after\nLIGHTER_API_SECRET=0x<80 hex characters representing 40 bytes>","handlingStrategy":"validation","validationCode":"let hex_str = secret.trim().trim_start_matches(\"0x\").trim_start_matches(\"0X\");\nassert_eq!(hex_str.len(), 80, \"Lighter API secret must be 80 hex chars (40 bytes)\");","typeGuard":"fn is_40_byte_hex(s: &str) -> bool {\n    let h = s.trim().trim_start_matches(\"0x\").trim_start_matches(\"0X\");\n    h.len() == 80 && h.chars().all(|c| c.is_ascii_hexdigit())\n}","tryCatchPattern":"match decode_private_key_hex(&secret) {\n    Ok(key) => use_key(key),\n    Err(e) => eprintln!(\"Invalid LIGHTER_API_SECRET: {e:#}; expected 80 hex chars\"),\n}","preventionTips":["Check key length (80 hex chars) before deploying config.","Do not reuse Ethereum 32-byte private keys as Lighter API secrets.","Beware clipboard truncation when copying secrets."],"tags":["credentials","hex","validation","lighter"],"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-14T00:17:10.932Z"}