nautechsystems/nautilus_trader · error · anyhow::Error
Failed to batch insert into pool_flash_event table: {e}
Error message
Failed to batch insert into pool_flash_event table: {e} What it means
Raised when a batched UNNEST INSERT of pool flash events (with paid0/paid1 amounts) into the `pool_flash_event` table fails. The sqlx execution error is wrapped in anyhow with this message; the driver error text follows the prefix. The flash-event batch is not persisted when this fires.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:1861
",
)
.bind(&chain_ids[..])
.bind(&dex_names[..])
.bind(&pool_identifiers[..])
.bind(&blocks[..])
.bind(&transaction_hashes[..])
.bind(&transaction_indices[..])
.bind(&log_indices[..])
.bind(&senders[..])
.bind(&recipients[..])
.bind(&amount0s[..])
.bind(&amount1s[..])
.bind(&paid0s[..])
.bind(&paid1s[..])
.execute(&self.pool)
.await
.map(|_| ())
.map_err(|e| anyhow::anyhow!("Failed to batch insert into pool_flash_event table: {e}"))
}
/// Inserts multiple pool fee-protocol update events in a single database operation using UNNEST.
///
/// # Errors
///
/// Returns an error if the database operation fails.
pub async fn add_pool_fee_protocol_updates_batch(
&self,
chain_id: u32,
updates: &[PoolFeeProtocolUpdate],
) -> anyhow::Result<()> {
if updates.is_empty() {
return Ok(());
}
// Prepare vectors for each column
let len = updates.len();View on GitHub (pinned to 18893faf8b)
Solutions
- Inspect the wrapped `{e}` suffix for the underlying sqlx/PostgreSQL error
- Confirm all bound arrays (identifiers, blocks, amounts, paid0s, paid1s, ...) share the same length
- Run schema migrations so `pool_flash_event` columns match the bound fields
- Check database connectivity and pool configuration
- Add ON CONFLICT handling or deduplicate the batch if unique-constraint errors appear
Example fix
// before
.map_err(|e| anyhow::anyhow!("Failed to batch insert into pool_flash_event table: {e}"))
// after: guard against length mismatch before execution
assert_eq!(paid0s.len(), paid1s.len());
.map_err(|e| anyhow::anyhow!("Failed to batch insert into pool_flash_event table (rows={}): {e}", paid0s.len())) Defensive patterns
Strategy: try-catch
Validate before calling
fn validate_flash_batch(rows: &[PoolFlashEvent]) -> anyhow::Result<()> {
anyhow::ensure!(!rows.is_empty(), "empty flash batch");
anyhow::ensure!(rows.iter().all(|r| !r.transaction_hash.is_empty()), "missing tx hash");
Ok(())
} Type guard
fn is_non_empty(s: &Option<String>) -> bool { s.as_ref().map_or(false, |v| !v.is_empty()) } Try / catch
if let Err(e) = insert_pool_flash_events(&events).await {
tracing::error!("flash batch insert failed: {e:#}");
if is_transient(&e) { retry_with_backoff(&events).await?; }
} Prevention
- Check amounts fit the numeric column precision before binding
- Run migrations before starting indexing runs
- Deduplicate events per (block, tx, log index) to avoid unique conflicts
- Health-check the pool before large batch writes
When it happens
Trigger: Executing the multi-row flash-event insert (`.bind(&amount1s[..])`, `.bind(&paid0s[..])`, `.bind(&paid1s[..])`, `.execute(&self.pool)`) when Postgres rejects it: constraint violation, type coercion failure on amount strings, array-length mismatch, or connection failure.
Common situations: Amount strings exceeding the numeric column's precision after a schema change; mismatched vector lengths when the event batch is assembled; dead pool connections after idle timeouts; concurrent writers causing unique/PK conflicts.
Related errors
- Failed to batch insert into pool_fee_collect table: {e}
- Failed to batch insert into pool_fee_protocol_update_event t
- Failed to batch insert into pool_fee_protocol_collect_event
- Failed to batch insert into pool_position table: {e}
- Failed to batch insert into pool_tick table: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/0b4b44ffbd611939.
Report an issue: GitHub.