nautechsystems/nautilus_trader · error · anyhow::Error

No durable store configured for execution reconciliation

Error message

No durable store configured for execution reconciliation

What it means

reconcile_unresolved_execution (crates/adapters/blockchain/src/execution/client.rs:907) fails closed when cache.database is None: crash recovery of in-flight execution requires the durable intent store, so reconciliation without it is refused rather than skipped. In the normal flow connect() guards this call with cache.has_database() (the store is attached only when postgres_cache_database_config is set), so hitting the error means the method ran against a store-less cache - a direct invocation, a reconfigured client, or a code path that bypassed connect().

Source

Thrown at crates/adapters/blockchain/src/execution/client.rs:909

        Ok(SwapPlan {
            order,
            quote_currency,
            pool,
            instrument_id,
            pool_address,
            router: Address::from_str(&intent.transaction_to)?,
            token_in,
            token_out,
            fee,
            amount_in,
            min_amount_out: U256::ZERO,
            profiler_block: intent.created_block,
        })
    }

    async fn reconcile_unresolved_execution(&self) -> anyhow::Result<()> {
        let database = self.cache.database.clone().ok_or_else(|| {
            anyhow::anyhow!("No durable store configured for execution reconciliation")
        })?;
        let wallet_address = self.wallet_address.to_string();
        let Some(intent) = database
            .get_active_execution_intent(self.chain.chain_id, &wallet_address)
            .await?
        else {
            return Ok(());
        };
        anyhow::ensure!(
            intent.schema_version == crate::execution::transaction::EXECUTION_SCHEMA_VERSION,
            "Execution intent {} uses unsupported schema version {}",
            intent.id,
            intent.schema_version
        );

        if matches!(intent.status.as_str(), "prepared" | "signed") {
            database
                .mark_execution_intent_recoverable(intent.id)

View on GitHub (pinned to 2114cf6f76)

Solutions

  1. Set BlockchainExecutionClientConfig::postgres_cache_database_config before constructing the client.
  2. Call connect() and let it drive reconciliation itself - it attaches the store and calls this method with the guard in place.
  3. Verify cache.has_database() is true before any manual reconciliation call.
  4. Check connect() logs for the "No Postgres cache database configured" warning to catch the misconfiguration early.

Example fix

// before
let config = BlockchainExecutionClientConfig {
    postgres_cache_database_config: None,
    ..Default::default()
};
// client.reconcile_unresolved_execution() -> "No durable store configured for execution reconciliation"

// after
let config = BlockchainExecutionClientConfig {
    postgres_cache_database_config: Some(postgres_connect_options),
    ..Default::default()
};
// connect() attaches the store and runs reconciliation guarded by has_database()
Defensive patterns

Strategy: validation

Validate before calling

// Rust: reconciliation needs the durable store - verify before calling
anyhow::ensure!(
    client.cache.has_database(),
    "reconciliation requires postgres_cache_database_config"
);

Type guard

fn is_no_durable_store_for_reconciliation(e: &anyhow::Error) -> bool {
    e.to_string()
        .contains("No durable store configured for execution reconciliation")
}

Try / catch

match client.reconcile_unresolved_execution().await {
    Ok(()) => {}
    Err(e) if is_no_durable_store_for_reconciliation(&e) => {
        // configuration defect: attach Postgres and reconnect; do not skip reconciliation
        return Err(e.context("set postgres_cache_database_config before recovery"));
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Invoking reconcile_unresolved_execution on a client whose config omitted postgres_cache_database_database style options (postgres_cache_database_config: None), so connect() never attached a store; calling reconciliation manually on a freshly constructed, unconnected client; config where Postgres options failed to parse and the field defaulted to None.

Common situations: Missing Postgres environment variables in recovery tooling; ops scripts constructing a client just to reconcile without wiring the durable store; deployments that previously ran without persistence now attempting reconciliation.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@2114cf6f76 (2026-08-21). Data as JSON: /api/errors/0f94fff5e85628db. Report an issue: GitHub.