nautechsystems/nautilus_trader · error · anyhow::Error

Failed to create signing key: {e}

Error message

Failed to create signing key: {e}

What it means

Because SigningKey does not implement Clone, account_offline re-creates the secp256k1 signing key from the wallet's stored private key bytes each time it needs a cosmos Account. This error is returned if that reconstruction fails, meaning the stored bytes are no longer (or never were) a valid key scalar.

Source

Thrown at crates/adapters/dydx/src/execution/wallet.rs:122

            private_key_bytes: key_bytes.into_boxed_slice(),
            address,
            account_id,
        })
    }

    /// Get a dYdX account with zero account and sequence numbers.
    ///
    /// Creates an account using the pre-computed address/account_id.
    /// SigningKey is recreated from stored bytes (it doesn't implement Clone).
    /// Account and sequence numbers must be set before signing.
    ///
    /// # Errors
    ///
    /// Returns an error if the signing key creation fails.
    pub fn account_offline(&self) -> Result<Account, anyhow::Error> {
        // SigningKey doesn't impl Clone, so recreate from stored bytes
        let key = SigningKey::from_slice(&self.private_key_bytes)
            .map_err(|e| anyhow::anyhow!("Failed to create signing key: {e}"))?;

        Ok(Account {
            address: self.address.clone(),
            account_id: self.account_id.clone(),
            key,
            account_number: 0,
            sequence_number: 0,
        })
    }

    /// Returns the pre-computed wallet address.
    #[must_use]
    pub fn address(&self) -> &str {
        &self.address
    }
}

/// Represents a dYdX account.

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Re-create the wallet via DydxWallet::from_private_key with the original hex key instead of reconstructing it manually.
  2. Verify the stored private_key_bytes are exactly 32 bytes and within the valid secp256k1 range.
  3. If persisted state is corrupted, re-export the key from its source and rebuild the wallet.
  4. Ensure any serialization/deserialization of the wallet does not alter the key bytes (e.g. base64 vs hex confusion).

Example fix

// before: hand-built wallet with dummy bytes
// let wallet = DydxWallet { private_key_bytes: vec![0u8; 16].into_boxed_slice(), ... };
// let acct = wallet.account_offline()?; // fails
// after
let wallet = DydxWallet::from_private_key(&hex_key)?;
let acct = wallet.account_offline()?;
Defensive patterns

Strategy: validation

Validate before calling

assert_eq!(wallet_key_bytes.len(), 32, "stored key must be 32 bytes");

Try / catch

let account = wallet.account_offline()
    .map_err(|e| {
        log::error!("signing key reconstruction failed: {e}; rebuild wallet from hex key");
        e
    })?;

Prevention

When it happens

Trigger: Calling DydxWallet::account_offline when SigningKey::from_slice(&self.private_key_bytes) fails — corrupted or wrongly-sized private_key_bytes, or a wallet constructed through a path that skipped key validation.

Common situations: Deserializing a wallet from a snapshot/state file whose key bytes were truncated or corrupted; manually constructing the struct in tests with dummy bytes; memory corruption is rare — usually the stored bytes were never a valid 32-byte scalar.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/6d1545f05291e4b4. Report an issue: GitHub.