nautechsystems/nautilus_trader · critical · anyhow::Error
Signed transaction envelope does not use the database active
Error message
Signed transaction envelope does not use the database active key
What it means
This error is raised during protected persistence of a sealed (encrypted) signed transaction. The library locks the execution_payload_state row and requires that the envelope's embedded key id matches the database's active_key_id. If the caller signed the envelope with a rotated, stale, or foreign key, the transaction is rejected so plaintext or wrongly-keyed payloads never reach storage.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:6635
anyhow::anyhow!("Failed to start signed transaction persistence: {e}")
})?;
if let Some(envelope) = sealed_transaction {
let state_row = sqlx::query(
"SELECT deployment_id, protocol_version, operation, active_key_id \
FROM execution_payload_state WHERE component = 'signed_transactions' FOR SHARE",
)
.fetch_optional(&mut *transaction)
.await
.context("failed to lock execution payload state for protected persistence")?
.ok_or_else(|| anyhow::anyhow!("Execution payload protection is not active"))?;
let state = execution_payload_state_from_row(&state_row)?;
anyhow::ensure!(
state.protocol_version == EXECUTION_PAYLOAD_PROTOCOL_VERSION
&& state.operation == "ready",
"Execution payload storage is not ready for protected persistence"
);
anyhow::ensure!(
envelope_key_id(envelope)?.as_slice() == state.active_key_id.as_slice(),
"Signed transaction envelope does not use the database active key"
);
} else {
let marker = sqlx::query_scalar::<_, bool>(
"SELECT EXISTS (SELECT 1 FROM execution_schema_version WHERE component = $1)",
)
.bind(EXECUTION_PAYLOAD_COMPONENT)
.fetch_one(&mut *transaction)
.await
.context("failed to inspect execution payload marker")?;
anyhow::ensure!(
!marker,
"Plaintext signed transaction persistence is disabled after payload activation"
);
}
let current_status = sqlx::query_scalar::<_, String>(
"SELECT status FROM execution_intent WHERE id = $1 FOR UPDATE",View on GitHub (pinned to 18893faf8b)
Solutions
- Refresh the active key id from execution_payload_state (SELECT active_key_id ...) and re-seal the envelope before retrying persistence.
- Verify the process is connected to the intended database/environment; compare deployment_id and active_key_id with the target DB.
- If a rotation happened mid-flight, run the key-rotation/roll procedure so local signing material matches the new active key.
- Retry after re-sealing; do not persist the envelope under the old key.
Example fix
// before let envelope = seal_with(cached_key_id, payload); // stale key // after let active = load_active_key_id(pool).await?; let envelope = seal_with(active, payload); // re-seal with DB active key
Defensive patterns
Strategy: validation
Validate before calling
let state: (Vec<u8>,) = sqlx::query_as("SELECT active_key_id FROM execution_payload_state WHERE component='signed_transactions'").fetch_one(&pool).await?;
anyhow::ensure!(envelope_key_id(&envelope)?.as_slice() == state.0.as_slice(), "envelope key id != DB active key; re-seal before persisting"); Type guard
fn uses_active_key(envelope: &[u8], active_key_id: &[u8]) -> bool {
envelope_key_id(envelope).map(|k| k.as_slice() == active_key_id).unwrap_or(false)
} Try / catch
match db.add_execution_transaction(intent_id, chain_id, hash, sealed).await {
Err(e) if e.to_string().contains("does not use the database active key") => {
refresh_active_key(&pool).await?;
let resealed = seal(payload, active_key_id).await?;
db.add_execution_transaction(intent_id, chain_id, hash, resealed).await?;
}
result => result?,
} Prevention
- Fetch the active key id from the DB immediately before sealing, never cache across rotations
- Subscribe to key-rotation events and invalidate cached signing keys
- Tag envelopes with deployment_id and verify it matches the target database
- Add a pre-flight check comparing local key material with execution_payload_state on startup
When it happens
Trigger: Calling add_execution_transaction (or add_execution_transaction_payload with sealed_transaction=Some) while the envelope was encrypted with a key other than execution_payload_state.active_key_id for component 'signed_transactions'. Typically the DB was key-rotated (active_key_id changed) after the caller obtained/cached the old signing key, or the caller is writing to a different database than the one whose key produced the envelope.
Common situations: Key rotation performed on the DB between envelope creation and persistence; multiple environments (staging vs prod) sharing a transaction flow; a replica/mismatched connection pointing at a database with a different active key; process restart with a stale in-memory key cache.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- Plaintext signed transaction persistence is disabled after p
- Execution payload storage is in {operation} maintenance; com
- Execution payload protection is active, but no payload key i
- Execution payload storage is in {operation} maintenance, not
- Execution payload storage is in {operation} maintenance, not
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/f369d348ec2403e2.
Report an issue: GitHub.