nautechsystems/nautilus_trader · error · anyhow::Error
Signer not initialized; connect the client first
Error message
Signer not initialized; connect the client first
What it means
transaction_executor (crates/adapters/blockchain/src/execution/client.rs:797) requires an initialized signer; self.signer is populated only during connect(), which loads the private key from config.signer_private_key_env, validates it as hex, and verifies the derived address matches the configured wallet address. disconnect() clears the signer, and connect() itself resets it to None if any post-signer step (execution reconciliation, wallet balance refresh) fails. The error therefore means a signing operation ran outside a fully established connection, and the root cause is usually an earlier connect failure or a missing call.
Source
Thrown at crates/adapters/blockchain/src/execution/client.rs:797
);
}
Ok(pool)
}
/// Builds the shared transaction executor from the connected client state.
///
/// # Errors
///
/// Returns an error if no durable store is configured or the signer is not initialized.
fn transaction_executor(&self) -> anyhow::Result<TransactionExecutor> {
let database = self.cache.database.clone().ok_or_else(|| {
anyhow::anyhow!("No durable store configured; refusing to submit a transaction")
})?;
let signer = self
.signer
.clone()
.ok_or_else(|| anyhow::anyhow!("Signer not initialized; connect the client first"))?;
Ok(TransactionExecutor {
http_rpc_client: self.http_rpc_client.clone(),
database,
signer,
in_flight: Arc::clone(&self.in_flight),
wallet_balance: Arc::clone(&self.wallet_balance),
account_id: self.core.account_id,
wallet_address: self.wallet_address,
chain_id: self.chain.chain_id,
max_fee_per_gas_wei: self.config.max_fee_per_gas_wei,
base_fee_buffer_bps: self.config.base_fee_buffer_bps,
gas_limit: self.config.gas_limit,
gas_buffer_bps: self.config.gas_buffer_bps,
receipt_timeout: receipt_timeout(self.transaction_limits.receipt_timeout_secs),
receipt_max_polls: receipt_max_polls(self.transaction_limits.receipt_timeout_secs),
})
}View on GitHub (pinned to 2114cf6f76)
Solutions
- Await client.connect() and check it returned Ok before issuing any transactional operation.
- If connect failed, read the original error it returned - signer-private-key-env not set, invalid hex key, wallet address mismatch, or reconciliation/balance failure are the usual causes.
- Set the env var named by config.signer_private_key_env to the hex private key whose address equals the configured wallet address.
- Do not submit after disconnect(); reconnect first.
Example fix
// before let mut client = BlockchainExecutionClient::new(/* ... */); client.wrap(amount_wei).await?; // error: signer not initialized // after let mut client = BlockchainExecutionClient::new(/* ... */); client.connect().await?; // loads signer from env, reconciles, refreshes balances client.wrap(amount_wei).await?;
Defensive patterns
Strategy: validation
Validate before calling
// Rust: only submit on a fully connected client
anyhow::ensure!(
client.is_connected(),
"blockchain client not connected; call connect() before submitting"
);
// also confirm the signer env var exists before starting the session
std::env::var(&config.signer_private_key_env)
.with_context(|| format!("set {} before connect", config.signer_private_key_env))?; Type guard
fn is_signer_uninitialized(e: &anyhow::Error) -> bool {
e.to_string().contains("Signer not initialized; connect the client first")
} Try / catch
match client.wrap(amount_wei).await {
Ok(hash) => hash,
Err(e) if is_signer_uninitialized(&e) => {
// lifecycle bug: reconnect, surfacing the original connect() error if any
client.connect().await?;
client.wrap(amount_wei).await
}
Err(e) => return Err(e),
} Prevention
- Gate every submission path behind an is_connected() assertion.
- Always await and check connect()'s result; its error explains why the signer was reset.
- Set and validate the signer env var (hex key matching wallet_address) before starting the client.
- Treat disconnect() as terminal for the session - never submit afterwards.
When it happens
Trigger: Calling wrap/approve/submit_order before connect(); calling them after disconnect(); connect() failed at reconciliation or refresh_wallet_balances (both reset signer to None and return the underlying error) and the caller ignored the failure and proceeded.
Common situations: Fire-and-forget startup that never awaits connect(); the signer env var unset or invalid, or its derived address mismatching wallet_address, causing connect to bail earlier; a disconnect triggered mid-session (stop command) followed by a late order submission.
Related errors
- A BacktestNode is required for the bars_with_fills chart
- Signer address {} derived from '{}' does not match configure
- Invalid state trigger {self} -> {trigger}
- Cannot set cache database while node is running, set it befo
- Active execution intent {intent_id} was not found
AI-assisted analysis of nautechsystems/nautilus_trader@2114cf6f76 (2026-08-21).
Data as JSON: /api/errors/0944dd8758ffc988.
Report an issue: GitHub.