nautechsystems/nautilus_trader · error · anyhow::Error
Failed to batch insert into pool_liquidity_event table: {e}
Error message
Failed to batch insert into pool_liquidity_event table: {e} What it means
UNNEST-based batch insert of liquidity (mint/burn) events into `pool_liquidity_event` failed; wrapped with this message. Thrown when the bulk liquidity-event statement fails to execute against Postgres.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:1133
.bind(&chain_ids[..])
.bind(&dex_names[..])
.bind(&pool_identifiers[..])
.bind(&blocks[..])
.bind(&transaction_hashes[..])
.bind(&transaction_indices[..])
.bind(&log_indices[..])
.bind(&event_types[..])
.bind(&senders[..])
.bind(&owners[..])
.bind(&position_liquidities[..])
.bind(&amount0s[..])
.bind(&amount1s[..])
.bind(&tick_lowers[..])
.bind(&tick_uppers[..])
.execute(&self.pool)
.await
.map(|_| ())
.map_err(|e| anyhow::anyhow!("Failed to batch insert into pool_liquidity_event table: {e}"))
}
/// Adds or updates a token record in the database.
///
/// # Errors
///
/// Returns an error if the database operation fails.
pub async fn add_token(&self, token: &Token) -> anyhow::Result<()> {
sqlx::query(
"
INSERT INTO token (
chain_id, address, name, symbol, decimals
) VALUES ($1, $2, $3, $4, $5)
ON CONFLICT (chain_id, address)
DO UPDATE
SET
name = $3,
symbol = $4,View on GitHub (pinned to 18893faf8b)
Solutions
- Inspect the wrapped `{e}` for the specific constraint, cast, or FK failure
- Insert dependent block/pool rows before the liquidity events
- Deduplicate or use ON CONFLICT to tolerate replays
- Verify equal slice lengths for all bound arrays and that ticks fit the column's integer range
Example fix
// before assert!(amount0s.len() == amount1s.len()); // after assert!(amount0s.len() == amount1s.len() && amount0s.len() == tick_lowers.len() && tick_lowers.len() == tick_uppers.len());
Defensive patterns
Strategy: validation
Validate before calling
let n = events.len(); anyhow::ensure!(amount0s.len() == n && amount1s.len() == n && tick_lowers.len() == n && tick_uppers.len() == n, "liquidity batch arrays misaligned"); anyhow::ensure!(tick_lowers.iter().zip(tick_uppers).all(|(l, u)| l <= &i32::MAX as _ && u <= &i32::MAX as _), "tick out of i32 range");
Type guard
fn liquidity_batch_valid(events: &[LiquidityEvent]) -> bool {
events.iter().all(|e| e.tick_lower <= e.tick_upper && e.amount1 >= 0)
} Try / catch
match db.insert_liquidity_events_batch(&events).await {
Err(e) if e.to_string().contains("duplicate key") => tracing::debug!("liquidity events already persisted"),
Err(e) => return Err(e.context("liquidity event batch insert failed"),),
Ok(()) => {}
} Prevention
- Order persistence so parent block/pool rows exist before liquidity events
- Verify tick values fit the column's integer range before binding
- Keep all UNNEST slices built from a single loop over the events
- Make inserts idempotent for crash-recovery replays
When it happens
Trigger: Calling the batch liquidity insert with arrays of differing lengths (amount1s/tick_lowers/tick_uppers vs. other bound arrays), tick values out of the column's range, FK violation when the pool/block rows don't exist, or Postgres connectivity/permission failure during execute.
Common situations: Liquidity events persisted before their parent blocks in a crash-recovery path; replayed events tripping unique constraints; tick values from exotic pools exceeding INT column bounds; un-migrated fresh database.
Related errors
- Failed to batch insert into pool table: {e}
- Failed to batch insert into pool_swap_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/e5c6ede113eaa522.
Report an issue: GitHub.