nautechsystems/nautilus_trader · error
Verified action requires a decision class and evidence
Error message
Verified action requires a decision class and evidence
What it means
A validation guard thrown when an ExecutionVerificationBatch has an empty (or whitespace-only) decision_class or an empty decisions list. The library refuses to record a verified action without both a decision classification and at least one piece of decision evidence.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:6048
anyhow::ensure!(
result.rows_affected() == 1,
"Execution intent {} is not prepared for canonical nonce {}",
assignment.intent_id,
assignment.nonce
);
transaction
.commit()
.await
.map_err(|e| anyhow::anyhow!("Failed to commit verified nonce assignment: {e}"))?;
Ok(())
}
/// Appends one verified decision batch before an action on an existing active intent.
pub(crate) async fn record_execution_verification_batch(
&self,
batch: &ExecutionVerificationBatch<'_>,
) -> anyhow::Result<()> {
anyhow::ensure!(
!batch.decision_class.trim().is_empty() && !batch.decisions.is_empty(),
"Verified action requires a decision class and evidence"
);
anyhow::ensure!(
batch.provider_ids.len() == 3
&& batch.operator_ids.len() == 3
&& batch.failure_domain_ids.len() >= 3,
"Verified action requires the configured provider identities"
);
let chain_id = i32::try_from(batch.chain_id)
.context("Verification chain ID exceeds PostgreSQL INTEGER")?;
let nonce =
i64::try_from(batch.nonce).context("Execution nonce exceeds PostgreSQL BIGINT")?;
let mut transaction = self
.pool
.begin()
.await
.map_err(|e| anyhow::anyhow!("Failed to start verified action evidence: {e}"))?;View on GitHub (pinned to 18893faf8b)
Solutions
- Populate batch.decision_class with the intended classification string before calling record_execution_verification_batch.
- Ensure at least one decision entry is present in batch.decisions.
- Trim and validate inputs at the batch construction site so empty values fail early with a clearer message.
Example fix
// before
let batch = ExecutionVerificationBatch { decision_class: collected_class.trim().to_string(), decisions: decisions, .. };
// after
anyhow::ensure!(!collected_class.trim().is_empty(), "decision_class required");
anyhow::ensure!(!decisions.is_empty(), "at least one decision required");
let batch = ExecutionVerificationBatch { decision_class: collected_class.trim().to_string(), decisions, .. }; Defensive patterns
Strategy: validation
Validate before calling
// Rust
fn validate_batch_basic(batch: &ExecutionVerificationBatch) -> Result<(), String> {
if batch.decision_class.trim().is_empty() { return Err("decision_class is empty".into()); }
if batch.decisions.is_empty() { return Err("decisions is empty".into()); }
Ok(())
} Type guard
fn has_evidence(batch: &ExecutionVerificationBatch) -> bool {
!batch.decision_class.trim().is_empty() && !batch.decisions.is_empty()
} Prevention
- Construct batches through a builder that rejects empty fields.
- Unit-test batch assembly with empty-input cases.
- Trim config/env-sourced strings at load time.
When it happens
Trigger: Calling record_execution_verification_batch with batch.decision_class == ""/whitespace or batch.decisions.is_empty().
Common situations: A caller assembling the batch programmatically forgets to fill in decision_class; an upstream collector produced no decisions but the pipeline still tried to record the batch; string fields populated from trimmed-empty env/config values.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/6ded942ef20d6064.
Report an issue: GitHub.