nautechsystems/nautilus_trader · error · anyhow::Error
Failed to batch insert into pool_swap_event table: {e}
Error message
Failed to batch insert into pool_swap_event table: {e} What it means
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.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:1039
.bind(&transaction_hashes[..])
.bind(&transaction_indices[..])
.bind(&log_indices[..])
.bind(&senders[..])
.bind(&recipients[..])
.bind(&sqrt_price_x96s[..])
.bind(&liquidities[..])
.bind(&ticks[..])
.bind(&amount0s[..])
.bind(&amount1s[..])
.bind(&order_sides[..])
.bind(&base_quantities[..])
.bind("e_quantities[..])
.bind(&spot_prices[..])
.bind(&execution_prices[..])
.execute(&self.pool)
.await
.map(|_| ())
.map_err(|e| anyhow::anyhow!("Failed to batch insert into pool_swap_event table: {e}"))
}
/// Inserts multiple pool liquidity updates in a single database operation using UNNEST for optimal performance.
///
/// # Errors
///
/// Returns an error if the database operation fails.
pub async fn add_pool_liquidity_updates_batch(
&self,
chain_id: u32,
updates: &[PoolLiquidityUpdate],
) -> anyhow::Result<()> {
if updates.is_empty() {
return Ok(());
}
// Prepare vectors for each column
let len = updates.len();View on GitHub (pinned to 18893faf8b)
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
Example fix
// before db.insert_swap_events(&events).await?; // fails: block rows missing // after db.insert_blocks(&blocks_for(events)).await?; db.insert_swap_events(&events).await?;
Defensive patterns
Strategy: validation
Validate before calling
let n = events.len(); anyhow::ensure!(quote_quantities.len() == n && spot_prices.len() == n && execution_prices.len() == n, "swap batch arrays misaligned"); // parents must exist first anyhow::ensure!(blocks_inserted && pools_inserted, "insert blocks/pools before swap events");
Type guard
fn swap_batch_valid(events: &[SwapEvent]) -> bool {
events.iter().all(|e| e.log_index >= 0 && !e.tx_hash.is_empty())
} Try / catch
match db.insert_swap_events_batch(&events).await {
Err(e) if e.to_string().contains("duplicate key") => tracing::debug!("swap events already persisted"),
Err(e) => return Err(e.context("swap event batch insert failed")),
Ok(()) => {}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Failed to batch insert into pool table: {e}
- Failed to batch insert into pool_liquidity_event table: {e}
- Failed to insert into block table: {e}
- Failed to batch insert into block table: {e}
- Failed to batch insert into pool_event_block table: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/dabcb9baedab6dd4.
Report an issue: GitHub.