nautechsystems/nautilus_trader · error

Failed to insert into general table: {e}

Error message

Failed to insert into general table: {e}

What it means

`DatabaseQueries::add` inserts a raw key-value entry into the `general` table and wraps any sqlx failure in this anyhow error. Typical underlying causes are connection failure, a missing `general` table (migrations not applied), or a constraint violation such as a duplicate primary key when the same key is inserted twice.

Source

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

            .execute(pool)
            .await
            .map(|_| ())
            .map_err(|e| anyhow::anyhow!("Failed to truncate tables: {e}"))
    }

    /// Inserts a raw key-value entry into the `general` table via the provided `pool`.
    ///
    /// # Errors
    ///
    /// Returns an error if the INSERT operation fails.
    pub async fn add(pool: &PgPool, key: String, value: Vec<u8>) -> anyhow::Result<()> {
        sqlx::query("INSERT INTO general (id, value) VALUES ($1, $2)")
            .bind(key)
            .bind(value)
            .execute(pool)
            .await
            .map(|_| ())
            .map_err(|e| anyhow::anyhow!("Failed to insert into general table: {e}"))
    }

    /// Loads all entries from the `general` table via the provided `pool`.
    ///
    /// # Errors
    ///
    /// Returns an error if the SELECT operation fails.
    pub async fn load(pool: &PgPool) -> anyhow::Result<AHashMap<String, Vec<u8>>> {
        sqlx::query_as::<_, GeneralRow>("SELECT * FROM general")
            .fetch_all(pool)
            .await
            .map(|rows| {
                let mut cache: AHashMap<String, Vec<u8>> = AHashMap::new();
                for row in rows {
                    cache.insert(row.id, row.value);
                }
                cache
            })

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Run migrations so the `general` table exists.
  2. Check the inner sqlx error: for duplicate-key errors, delete the row first or use ON CONFLICT upsert semantics.
  3. Verify pool connectivity and that the DB user has INSERT privilege on `general`.
  4. Confirm key/value encodings match the column types (e.g. text/bytea).

Example fix

// before
DatabaseQueries::add(&pool, key, value).await?;
// after
sqlx::query("DELETE FROM general WHERE id = $1").bind(&key).execute(&pool).await?;
DatabaseQueries::add(&pool, key, value).await?;
Defensive patterns

Strategy: try-catch

Validate before calling

let exists: bool = sqlx::query_scalar(
    "SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'general')",
).fetch_one(pool).await?;
if !exists {
    return Err(anyhow::anyhow!("'general' table missing — run migrations"));
}

Try / catch

match DatabaseQueries::add(&pool, key, value).await {
    Err(e) if e.to_string().contains("duplicate key") => {
        tracing::warn!("key {key} already present, skipping");
    }
    result => result?,
}

Prevention

When it happens

Trigger: Calling `DatabaseQueries::add(pool, key, value)` when the connection/pool is broken, the `general` table doesn't exist, the `id` primary key is violated by a repeated key, or the `value` bytes don't fit the column type.

Common situations: Writing cache entries to a database where migrations never ran; re-adding an existing key on restart; Postgres restart or network blip dropping the connection mid-write.

Related errors


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