{"record":{"id":"18532be420f847ed","repo":"nautechsystems/nautilus_trader","slug":"failed-to-load-pool-positions-e","errorCode":null,"errorMessage":"Failed to load pool positions: {e}","messagePattern":"Failed to load pool positions: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/cache/database.rs","lineNumber":2679,"sourceCode":"                tokens_owed_0::TEXT, tokens_owed_1::TEXT,\n                total_amount0_deposited::TEXT, total_amount1_deposited::TEXT,\n                total_amount0_collected::TEXT, total_amount1_collected::TEXT\n            FROM pool_position\n            WHERE chain_id = $1\n            AND pool_identifier = $2\n            AND snapshot_block = $3\n            AND snapshot_transaction_index = $4\n            AND snapshot_log_index = $5\n            \",\n        )\n        .bind(chain_id as i32)\n        .bind(pool_identifier.as_ref())\n        .bind(snapshot_block as i64)\n        .bind(snapshot_transaction_index as i32)\n        .bind(snapshot_log_index as i32)\n        .fetch_all(&self.pool)\n        .await\n        .map_err(|e| anyhow::anyhow!(\"Failed to load pool positions: {e}\"))?;\n\n        rows.iter()\n            .map(|row| {\n                let owner: String = row.get(\"owner\");\n                let position = PoolPosition {\n                    owner: validate_address(&owner)?,\n                    tick_lower: row.get(\"tick_lower\"),\n                    tick_upper: row.get(\"tick_upper\"),\n                    liquidity: row.get::<String, _>(\"liquidity\").parse()?,\n                    fee_growth_inside_0_last: row\n                        .get::<String, _>(\"fee_growth_inside_0_last\")\n                        .parse()?,\n                    fee_growth_inside_1_last: row\n                        .get::<String, _>(\"fee_growth_inside_1_last\")\n                        .parse()?,\n                    tokens_owed_0: row.get::<String, _>(\"tokens_owed_0\").parse()?,\n                    tokens_owed_1: row.get::<String, _>(\"tokens_owed_1\").parse()?,\n                    total_amount0_deposited: row","sourceCodeStart":2661,"sourceCodeEnd":2697,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/cache/database.rs#L2661-L2697","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","solutions":["Check database reachability and credentials with the same URL the adapter uses.","Apply pending migrations so the positions table and columns match the adapter's expectations.","Inspect the chained `{e}` for the precise SQLSTATE/driver cause.","If timeouts recur on managed Postgres, raise the pool's acquire timeout or reduce concurrent load.","Verify the `positions` table has indexes on (pool_identifier, snapshot watermark) so large fetches don't time out."],"exampleFix":"// before: no timeout handling\nlet rows = store.load_positions(...).await?;\n// after: guard against transient/network errors\nlet rows = tokio::time::timeout(Duration::from_secs(30), store.load_positions(...)).await\n    .map_err(|_| anyhow::anyhow!(\"positions load timed out\"))??;","handlingStrategy":"try-catch","validationCode":"// confirm schema before loading\nsqlx::query(\"SELECT 1 FROM pool_positions LIMIT 1\").execute(&db).await?;","typeGuard":null,"tryCatchPattern":"match store.load_positions(...).await {\n    Ok(positions) => positions,\n    Err(e) if is_timeout_or_transient(&e) => retry_with_backoff(|| store.load_positions(...), 3).await,\n    Err(e) => return Err(e),\n}","preventionTips":["Run migrations before adapter startup so the positions table exists with expected columns.","Set explicit acquire/statement timeouts on the sqlx pool sized for your Postgres latency.","Index positions by (pool_identifier, snapshot watermark) to keep large fetches fast.","Monitor DB connectivity with health checks and alerts."],"tags":["database","postgres","sqlx","positions-loading"],"backgroundTag":"database-query-failed","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}