nautechsystems/nautilus_trader · error

Failed to load position ids: {e}

Error message

Failed to load position ids: {e}

What it means

load_positions first queries the distinct position_ids from the position events table; if that SELECT fails the sqlx error is wrapped in this message. No positions are returned because the id enumeration itself failed.

Source

Thrown at crates/infrastructure/src/sql/queries.rs:991

    /// # Errors
    ///
    /// Returns an error if loading position IDs or replaying any position fails.
    pub async fn load_positions(pool: &PgPool) -> anyhow::Result<Vec<Position>> {
        let position_ids: Vec<PositionId> = sqlx::query(
            r#"
            SELECT DISTINCT position_id
            FROM "position_event"
            ORDER BY position_id ASC
        "#,
        )
        .fetch_all(pool)
        .await
        .map(|rows| {
            rows.into_iter()
                .map(|row| PositionId::from(row.get::<&str, _>(0)))
                .collect()
        })
        .map_err(|e| anyhow::anyhow!("Failed to load position ids: {e}"))?;

        let mut positions = Vec::new();

        for id in position_ids {
            match Self::load_position(pool, &id).await {
                Ok(Some(position)) => positions.push(position),
                Ok(None) => log::error!("Position not found: {id}"),
                Err(e) => log::error!("Failed to load position {id}: {e}"),
            }
        }

        Ok(positions)
    }

    async fn insert_position_event(
        transaction: &mut Transaction<'_, Postgres>,
        event: &OrderFilled,
    ) -> anyhow::Result<()> {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Inspect the wrapped sqlx error for the root cause.
  2. Run the nautilus SQL migrations so the position tables exist.
  3. Confirm the venue/instance filters match data actually written to this database.
  4. Verify connectivity and privileges, then retry if the failure was transient.
Defensive patterns

Strategy: retry

Validate before calling

sqlx::query("SELECT 1 FROM position_event LIMIT 1").fetch_optional(&pool).await?;

Try / catch

match load_positions(&pool, &venue).await {
    Err(e) if is_transient_db_error(&e) => retry_with_backoff(3),
    other => other,
}

Prevention

When it happens

Trigger: Calling DatabaseQuery::load_positions(pool, venue) and the id query errors: missing table, connection failure, or SQL error wrapped via anyhow.

Common situations: Database not migrated; wrong venue filter against an empty/uninitialized schema; connectivity loss at query time; insufficient SELECT privileges.

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/553c65375eb8aa3c. Report an issue: GitHub.