{"record":{"id":"559d15fa9bb91d39","repo":"nautechsystems/nautilus_trader","slug":"no-durable-store-configured-refusing-to-persist-e","errorCode":null,"errorMessage":"No durable store configured; refusing to persist execution transaction","messagePattern":"No durable store configured; refusing to persist execution transaction","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/cache/mod.rs","lineNumber":161,"sourceCode":"    /// # Errors\n    ///\n    /// Returns an error if no database is configured or the database operation fails.\n    #[expect(\n        clippy::too_many_arguments,\n        reason = \"the parameters mirror the persisted execution transaction fields\"\n    )]\n    pub async fn add_execution_transaction(\n        &self,\n        chain_id: u32,\n        wallet_address: &str,\n        nonce: u64,\n        transaction_hash: &str,\n        purpose: &str,\n        status: &str,\n        client_order_id: Option<&str>,\n    ) -> anyhow::Result<()> {\n        let database = self.database.as_ref().ok_or_else(|| {\n            anyhow::anyhow!(\n                \"No durable store configured; refusing to persist execution transaction\"\n            )\n        })?;\n\n        database\n            .add_execution_transaction(\n                chain_id,\n                wallet_address,\n                nonce,\n                transaction_hash,\n                purpose,\n                status,\n                client_order_id,\n            )\n            .await\n    }\n\n    /// Migrates the execution transaction table and installs its signer and order uniqueness","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/2114cf6f761429e0adb5ca9596fcd7b895b16011/crates/adapters/blockchain/src/cache/mod.rs#L143-L179","documentation":"BlockchainCache::add_execution_transaction (crates/adapters/blockchain/src/cache/mod.rs:161) fails closed when the cache has no attached durable store: the database: Option<BlockchainCacheDatabase> field is None, so the method refuses to persist an execution transaction instead of silently dropping the record. A database is only attached via BlockchainCache::initialize_database(PgConnectOptions), or automatically by BlockchainExecutionClient::connect when config.postgres_cache_database_config is set. This is an intentional safety design: durable transaction records are mandatory for crash recovery, so a missing store aborts the write path.","triggerScenarios":"Calling add_execution_transaction on a cache constructed with BlockchainCache::new(chain) without ever calling initialize_database; running the execution client with postgres_cache_database_config: None (connect logs the warning \"No Postgres cache database configured; transactions will be refused\") and then submitting an order, wrap, or approve which tries to persist its transaction record.","commonSituations":"Postgres connection settings omitted from the environment/config file in a new deployment; a staging config copied to production with the database section stripped; running in memory-only mode by design but still invoking durable-write APIs; Postgres configured but connect() was never called, so the cache never attached a store.","solutions":["Set BlockchainExecutionClientConfig::postgres_cache_database_config (PostgresConnectOptions) before constructing/connecting the execution client.","If using BlockchainCache directly, call cache.initialize_database(pg_connect_options).await before any execution-transaction write.","Confirm connect() succeeded; a failed Postgres connect surfaces earlier as \"Failed to connect to the Postgres cache database\".","Add a startup assertion on cache.has_database() so misconfiguration fails fast at boot instead of at first submission."],"exampleFix":"// before\nlet mut cache = BlockchainCache::new(chain);\ncache.add_execution_transaction(chain_id, wallet, nonce, tx_hash, purpose, status, None).await?; // errors: no durable store\n\n// after\nlet mut cache = BlockchainCache::new(chain);\ncache.initialize_database(pg_connect_options).await;\nassert!(cache.has_database());\ncache.add_execution_transaction(chain_id, wallet, nonce, tx_hash, purpose, status, None).await?;","handlingStrategy":"validation","validationCode":"// Rust: attach a durable store before any execution-transaction write\nif !cache.has_database() {\n    cache.initialize_database(pg_connect_options).await;\n}\nanyhow::ensure!(\n    cache.has_database(),\n    \"cannot persist execution transactions without Postgres\"\n);","typeGuard":"fn is_no_durable_store(e: &anyhow::Error) -> bool {\n    e.to_string().contains(\"No durable store configured\")\n}","tryCatchPattern":"if let Err(e) = cache.add_execution_transaction(/* ... */).await {\n    if is_no_durable_store(&e) {\n        // configuration bug: fail loudly at startup instead of retrying\n        return Err(e.context(\"execution persistence requires postgres_cache_database_config\"));\n    }\n    return Err(e);\n}","preventionTips":["Assert cache.has_database() (or client config has postgres options) as a hard startup check.","Treat a missing durable store as a boot-time failure for any deployment that will trade.","Watch connect() logs for the 'No Postgres cache database configured' warning and alert on it.","Keep one canonical config builder that always sets postgres_cache_database_config."],"tags":["postgres","configuration","durable-store","blockchain","rust"],"backgroundTag":"database-not-configured","analyzedSha":"2114cf6f761429e0adb5ca9596fcd7b895b16011","analyzedAt":"2026-08-21T11:28:30.864Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}