{"record":{"id":"dabcb9baedab6dd4","repo":"nautechsystems/nautilus_trader","slug":"failed-to-batch-insert-into-pool-swap-event-table","errorCode":null,"errorMessage":"Failed to batch insert into pool_swap_event table: {e}","messagePattern":"Failed to batch insert into pool_swap_event table: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/cache/database.rs","lineNumber":1039,"sourceCode":"        .bind(&transaction_hashes[..])\n        .bind(&transaction_indices[..])\n        .bind(&log_indices[..])\n        .bind(&senders[..])\n        .bind(&recipients[..])\n        .bind(&sqrt_price_x96s[..])\n        .bind(&liquidities[..])\n        .bind(&ticks[..])\n        .bind(&amount0s[..])\n        .bind(&amount1s[..])\n        .bind(&order_sides[..])\n        .bind(&base_quantities[..])\n        .bind(&quote_quantities[..])\n        .bind(&spot_prices[..])\n        .bind(&execution_prices[..])\n        .execute(&self.pool)\n        .await\n        .map(|_| ())\n        .map_err(|e| anyhow::anyhow!(\"Failed to batch insert into pool_swap_event table: {e}\"))\n    }\n\n    /// Inserts multiple pool liquidity updates in a single database operation using UNNEST for optimal performance.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the database operation fails.\n    pub async fn add_pool_liquidity_updates_batch(\n        &self,\n        chain_id: u32,\n        updates: &[PoolLiquidityUpdate],\n    ) -> anyhow::Result<()> {\n        if updates.is_empty() {\n            return Ok(());\n        }\n\n        // Prepare vectors for each column\n        let len = updates.len();","sourceCodeStart":1021,"sourceCodeEnd":1057,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/cache/database.rs#L1021-L1057","documentation":"The bulk INSERT of pool swap-event rows into the pool_swap_event table failed at the database level (e.g. constraint violation, connectivity, or type mismatch); the underlying sqlx error is wrapped so callers can see the batch failed and none of the rows in that batch were committed.","triggerScenarios":"Calling the batch swap insert with misaligned bound arrays (quote_quantities/spot_prices/execution_prices and the rest), numeric strings that fail Postgres numeric/decimal casts, FK violation when the referenced pool or block row is absent, or connection loss during execute.","commonSituations":"Swap events referencing blocks not yet inserted (FK violation due to out-of-order persistence); price/quantity serialized with precision formats rejected by NUMERIC; replayed events duplicating a unique (pool, block, tx, log_index) key; fresh DB without migrations.","solutions":["Read the wrapped `{e}` for the exact failing constraint or cast","Persist parent rows first (block, pool) before swap events so FKs resolve","Deduplicate events by their natural key or add ON CONFLICT DO NOTHING for replay tolerance","Ensure all bound slices are the same length and numeric values serialize to strings Postgres NUMERIC accepts"],"exampleFix":"// before\ndb.insert_swap_events(&events).await?; // fails: block rows missing\n// after\ndb.insert_blocks(&blocks_for(events)).await?;\ndb.insert_swap_events(&events).await?;","handlingStrategy":"validation","validationCode":"let n = events.len();\nanyhow::ensure!(quote_quantities.len() == n && spot_prices.len() == n && execution_prices.len() == n, \"swap batch arrays misaligned\");\n// parents must exist first\nanyhow::ensure!(blocks_inserted && pools_inserted, \"insert blocks/pools before swap events\");","typeGuard":"fn swap_batch_valid(events: &[SwapEvent]) -> bool {\n    events.iter().all(|e| e.log_index >= 0 && !e.tx_hash.is_empty())\n}","tryCatchPattern":"match db.insert_swap_events_batch(&events).await {\n    Err(e) if e.to_string().contains(\"duplicate key\") => tracing::debug!(\"swap events already persisted\"),\n    Err(e) => return Err(e.context(\"swap event batch insert failed\")),\n    Ok(()) => {}\n}","preventionTips":["Persist in dependency order: blocks → pools → swap events, ideally in one transaction","Use ON CONFLICT for idempotent re-processing after crashes","Serialize prices/quantities to NUMERIC-compatible strings","Assert equal array lengths before every UNNEST batch"],"tags":["database","postgres","sqlx","insert","batch"],"backgroundTag":"database-write-failed","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}