nautechsystems/nautilus_trader · error · anyhow::Error
Cannot create sign doc: {e}
Error message
Cannot create sign doc: {e} What it means
TxBuilder::build_transaction wraps the cosmos-sdk SignDoc::new failure with this message. SignDoc construction serializes the tx body, auth info, chain id, and account number into the canonical bytes that will be signed; it fails if those values cannot be encoded consistently. The anyhow context preserves the underlying prost/encoding error.
Source
Thrown at crates/adapters/dydx/src/grpc/builder.rs:164
let ext = TxExtension {
selected_authenticators: auth_ids.to_vec(),
};
builder.non_critical_extension_option(ext.to_any());
}
let tx_body = builder.finish();
let fee = fee.unwrap_or_else(|| {
self.calculate_fee(None)
.unwrap_or_else(|_| Self::default_fee())
});
let auth_info =
SignerInfo::single_direct(Some(account.public_key()), account.sequence_number)
.auth_info(fee);
let sign_doc = SignDoc::new(&tx_body, &auth_info, &self.chain_id, account.account_number)
.map_err(|e| anyhow::anyhow!("Cannot create sign doc: {e}"))?;
account.sign(sign_doc)
}
/// Build and simulate a transaction to estimate gas.
///
/// Returns the raw transaction bytes suitable for simulation.
///
/// # Errors
///
/// Returns an error if transaction building fails.
pub fn build_for_simulation(
&self,
account: &Account,
msgs: impl IntoIterator<Item = Any>,
) -> Result<Vec<u8>, anyhow::Error> {
let tx_raw = self.build_transaction(account, msgs, None, None)?;
tx_raw.to_bytes().map_err(|e| anyhow::anyhow!("{e}"))View on GitHub (pinned to 18893faf8b)
Solutions
- Log/print the inner `{e}` to see the underlying protobuf encoding error and fix the offending message in msgs.
- Verify the chain_id matches the target network (dydx-mainnet-1 vs dydx-dymension-testnet-3 etc.) and the account_number/sequence come from a fresh query_account.
- Reduce message size or split messages into multiple transactions if the tx body exceeds size limits.
- Check that prost/ibc-proto dependency versions align so the tx body and auth_info types serialize with the same schema.
Example fix
// before
let sign_doc = SignDoc::new(&tx_body, &auth_info, &self.chain_id, account.account_number)
.map_err(|e| anyhow::anyhow!("Cannot create sign doc: {e}"))?;
// after: validate inputs before signing
if msgs.is_empty() { anyhow::bail!("No messages to include in tx"); }
let sign_doc = SignDoc::new(&tx_body, &auth_info, &self.chain_id, account.account_number)
.map_err(|e| anyhow::anyhow!("Cannot create sign doc (chain_id={chain_id}): {e}"))?; Defensive patterns
Strategy: try-catch
Validate before calling
if msgs.is_empty() { return Err(anyhow!("no messages provided")); } Try / catch
match builder.build_transaction(&account, msgs, fee, memo) {
Ok(tx) => tx,
Err(e) if e.to_string().starts_with("Cannot create sign doc") => {
// refresh account state and retry once
let (num, seq) = client.query_address(addr).await?;
builder.build_transaction(&refreshed_account, msgs, fee, memo)
}
Err(e) => return Err(e),
} Prevention
- Refresh account_number/sequence from chain before every sign
- Keep proto/prost crate versions consistent
- Validate the message list is non-empty and well-formed before building
When it happens
Trigger: Calling build_transaction (directly or via build_for_simulation) when SignDoc::new fails to serialize the tx body/auth_info for the given chain_id and account_number — typically malformed tx messages, oversized payloads, or a protobuf encoding failure.
Common situations: A message list containing a proto message whose serialization exceeds limits; mismatched or malformed chain-id string; account sequence/number values that break encoding after a node reset or mismatch between local account state and chain state.
Related errors
- {e}
- Failed to sign transaction: {e}
- Query account request failure, account should exist
- The latest block is empty
- The block doesn't contain a header
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/ef648153cc44e382.
Report an issue: GitHub.