nautechsystems/nautilus_trader · error · anyhow::Error

Failed to batch insert into pool_fee_protocol_collect_event

Error message

Failed to batch insert into pool_fee_protocol_collect_event table: {e}

What it means

Raised when the batched UNNEST INSERT of pool protocol-fee collect events (amount0/amount1) into the `pool_fee_protocol_collect_event` table fails. The sqlx error is wrapped with this prefix; the driver's reason follows it. The event batch is not persisted when this fires.

Source

Thrown at crates/adapters/blockchain/src/cache/database.rs:2024

            ON CONFLICT (chain_id, transaction_hash, log_index) DO NOTHING
           ",
        )
        .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[..])
        .execute(&self.pool)
        .await
        .map(|_| ())
        .map_err(|e| {
            anyhow::anyhow!(
                "Failed to batch insert into pool_fee_protocol_collect_event table: {e}"
            )
        })
    }

    /// Adds a pool snapshot to the database.
    ///
    /// # Errors
    ///
    /// Returns an error if the database insert fails.
    pub async fn add_pool_snapshot(
        &self,
        chain_id: u32,
        dex_name: &DexType,
        pool_identifier: &PoolIdentifier,
        snapshot: &PoolSnapshot,
    ) -> anyhow::Result<()> {
        sqlx::query(

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Check the wrapped `{e}` tail for the underlying sqlx/PostgreSQL error
  2. Confirm amount0s/amount1s and all parallel arrays have identical lengths
  3. Run migrations to align the table schema with the adapter's expectations
  4. Deduplicate the batch or add ON CONFLICT handling for replayed logs
  5. Verify connectivity and pool configuration

Example fix

// before
.map_err(|e| {
    anyhow::anyhow!(
        "Failed to batch insert into pool_fee_protocol_collect_event table: {e}"
    )
})
// after: pre-validate and add row-count context
assert_eq!(amount0s.len(), amount1s.len());
.map_err(|e| anyhow::anyhow!("Failed to batch insert into pool_fee_protocol_collect_event table (rows={}): {e}", amount0s.len()))
Defensive patterns

Strategy: try-catch

Validate before calling

fn validate_protocol_collect_batch(rows: &[PoolFeeProtocolCollectEvent]) -> anyhow::Result<()> {
    anyhow::ensure!(!rows.is_empty(), "empty batch");
    anyhow::ensure!(rows.iter().all(|r| !r.pool_identifier.is_empty()), "empty pool id");
    Ok(())
}

Type guard

fn is_valid_amount(s: &str) -> bool { !s.is_empty() && s.chars().all(|c| c.is_ascii_digit()) }

Try / catch

if let Err(e) = insert_pool_fee_protocol_collects(&events).await {
    tracing::error!("protocol-collect batch failed: {e:#}");
    if is_transient(&e) { schedule_retry(&events); }
}

Prevention

When it happens

Trigger: Executing the protocol-fee-collect batch insert (`.bind(&amount0s[..])`, `.bind(&amount1s[..])`, `.execute(&self.pool)`) when the database rejects it: constraint or type errors on amount columns, mismatched array lengths, or connection failure.

Common situations: Amount strings not fitting numeric columns after schema changes; duplicate log replay conflicting with unique keys; stale connections after a Postgres restart; missing migrations in a fresh environment.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/7940c4877f6b73ed. Report an issue: GitHub.