{"record":{"id":"fd8102976d18a9e8","repo":"nautechsystems/nautilus_trader","slug":"signature-hex-called-before-sign","errorCode":null,"errorMessage":"signature_hex called before sign","messagePattern":"signature_hex called before sign","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/derive/src/signing/eip712.rs","lineNumber":235,"sourceCode":"            signer\n                .sign_hash_sync(&typed_data_hash)\n                .map_err(|e| TypedDataError::SigningFailed {\n                    message: e.to_string(),\n                })?;\n        let bytes = signature.as_bytes();\n        self.signature = Some(bytes);\n        Ok(bytes)\n    }\n\n    /// Returns the signature as a `0x`-prefixed 130-character hex string.\n    /// Panics if [`SignedAction::sign`] has not yet been called.\n    ///\n    /// # Panics\n    ///\n    /// Panics if [`SignedAction::sign`] has not been called.\n    #[must_use]\n    pub fn signature_hex(&self) -> String {\n        let bytes = self.signature.expect(\"signature_hex called before sign\");\n        format!(\"0x{}\", alloy_primitives::hex::encode(bytes))\n    }\n\n    /// Returns the signed action's subaccount id.\n    #[must_use]\n    pub const fn subaccount_id(&self) -> u64 {\n        self.ctx.subaccount_id\n    }\n\n    /// Returns the signed action's nonce.\n    #[must_use]\n    pub const fn nonce(&self) -> u64 {\n        self.ctx.nonce\n    }\n\n    /// Returns the signed action's session-key signer address.\n    #[must_use]\n    pub const fn signer_address(&self) -> Address {","sourceCodeStart":217,"sourceCodeEnd":253,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/derive/src/signing/eip712.rs#L217-L253","documentation":"`SignedAction::signature_hex` formats the ECDSA/EIP-712 signature as a `0x`-prefixed hex string, but the signature bytes are only populated by calling `sign` first. The struct stores `signature: Option<[u8; 65]>`, and reading it before signing panics. This is a documented API-ordering requirement (the doc comment states the panic condition).","triggerScenarios":"Building a `SignedAction` (e.g. via the action-construction helpers) and calling `signature_hex()` before invoking `sign(private_key)`; also when `sign` fails silently or is skipped on a code path (e.g. early return, conditional signing).","commonSituations":"Developers wiring up Derive authentication for the first time forget the sign step; test harnesses constructing actions for serialization checks call signature_hex on unsigned actions; refactors move the sign call after logging code that reads the signature.","solutions":["Always call `sign(&private_key)` on the SignedAction before `signature_hex()`.","Refactor to a typestate or a `sign` that returns a `SignedActionWithSignature` so unsigned actions cannot expose a signature.","If only the action payload is needed (hashing/serialization), use methods that don't require the signature.","Add an assertion or debug check before use to catch the ordering mistake early."],"exampleFix":"// before\nlet action = SignedAction { action, signature: None, .. };\nlet sig = action.signature_hex(); // panics\n// after\nlet mut action = SignedAction { action, signature: None, .. };\naction.sign(&private_key)?;\nlet sig = action.signature_hex();","handlingStrategy":"validation","validationCode":"debug_assert!(action.signature.is_some(), \"call sign() before signature_hex()\");\nif action.signature.is_none() {\n    return Err(/* signature not yet computed */);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always call sign(&private_key) before reading the signature","Order code so sign happens immediately after action construction","Consider a typestate API that separates SignedAction from SignedActionWithSignature"],"tags":["rust","panic","signing","eip712","api-misuse"],"backgroundTag":"invalid-state-transition","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"}