nautechsystems/nautilus_trader · error · anyhow::Error

Failed to load pool positions: {e}

Error message

Failed to load pool positions: {e}

What it means

This wraps a SQLx failure from the SELECT that fetches all liquidity-position rows belonging to a pool snapshot at its watermark (`fetch_all`). The positions cannot be loaded, so snapshot replay/restore aborts. Like the other wrappers, only actual query failures produce this — an empty result set is valid.

Source

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

                tokens_owed_0::TEXT, tokens_owed_1::TEXT,
                total_amount0_deposited::TEXT, total_amount1_deposited::TEXT,
                total_amount0_collected::TEXT, total_amount1_collected::TEXT
            FROM pool_position
            WHERE chain_id = $1
            AND pool_identifier = $2
            AND snapshot_block = $3
            AND snapshot_transaction_index = $4
            AND snapshot_log_index = $5
            ",
        )
        .bind(chain_id as i32)
        .bind(pool_identifier.as_ref())
        .bind(snapshot_block as i64)
        .bind(snapshot_transaction_index as i32)
        .bind(snapshot_log_index as i32)
        .fetch_all(&self.pool)
        .await
        .map_err(|e| anyhow::anyhow!("Failed to load pool positions: {e}"))?;

        rows.iter()
            .map(|row| {
                let owner: String = row.get("owner");
                let position = PoolPosition {
                    owner: validate_address(&owner)?,
                    tick_lower: row.get("tick_lower"),
                    tick_upper: row.get("tick_upper"),
                    liquidity: row.get::<String, _>("liquidity").parse()?,
                    fee_growth_inside_0_last: row
                        .get::<String, _>("fee_growth_inside_0_last")
                        .parse()?,
                    fee_growth_inside_1_last: row
                        .get::<String, _>("fee_growth_inside_1_last")
                        .parse()?,
                    tokens_owed_0: row.get::<String, _>("tokens_owed_0").parse()?,
                    tokens_owed_1: row.get::<String, _>("tokens_owed_1").parse()?,
                    total_amount0_deposited: row

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Check database reachability and credentials with the same URL the adapter uses.
  2. Apply pending migrations so the positions table and columns match the adapter's expectations.
  3. Inspect the chained `{e}` for the precise SQLSTATE/driver cause.
  4. If timeouts recur on managed Postgres, raise the pool's acquire timeout or reduce concurrent load.
  5. Verify the `positions` table has indexes on (pool_identifier, snapshot watermark) so large fetches don't time out.

Example fix

// before: no timeout handling
let rows = store.load_positions(...).await?;
// after: guard against transient/network errors
let rows = tokio::time::timeout(Duration::from_secs(30), store.load_positions(...)).await
    .map_err(|_| anyhow::anyhow!("positions load timed out"))??;
Defensive patterns

Strategy: try-catch

Validate before calling

// confirm schema before loading
sqlx::query("SELECT 1 FROM pool_positions LIMIT 1").execute(&db).await?;

Try / catch

match store.load_positions(...).await {
    Ok(positions) => positions,
    Err(e) if is_timeout_or_transient(&e) => retry_with_backoff(|| store.load_positions(...), 3).await,
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling the pool-positions loader when the DB is down, the positions table is missing/renamed (schema drift), bound column types no longer match (e.g. `snapshot_block` column type changed), or connection limits are exhausted.

Common situations: Postgres container stopped or restarted; adapter upgraded without running migrations; wrong database/URL env var pointing at a database without the positions schema; network partition to a remote managed Postgres (timeouts).

Understand the failure class

Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.

Related errors


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