nautechsystems/nautilus_trader · error · anyhow::Error

Failed to insert into token table: {e}

Error message

Failed to insert into token table: {e}

What it means

Database write error in the blockchain cache's token insert: the SQLx upsert of a token row (chain_id, address, name, symbol, decimals) into PostgreSQL failed; the driver error is embedded in the message.

Source

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

                chain_id, address, name, symbol, decimals
            ) VALUES ($1, $2, $3, $4, $5)
            ON CONFLICT (chain_id, address)
            DO UPDATE
            SET
                name = $3,
                symbol = $4,
                decimals = $5
        ",
        )
        .bind(token.chain.chain_id as i32)
        .bind(token.address.to_string())
        .bind(token.name.as_str())
        .bind(token.symbol.as_str())
        .bind(i32::from(token.decimals))
        .execute(&self.pool)
        .await
        .map(|_| ())
        .map_err(|e| anyhow::anyhow!("Failed to insert into token table: {e}"))
    }

    /// Records an invalid token address with associated error information.
    ///
    /// # Errors
    ///
    /// Returns an error if the database insertion fails.
    pub async fn add_invalid_token(
        &self,
        chain_id: u32,
        address: &Address,
        error_string: &str,
    ) -> anyhow::Result<()> {
        sqlx::query(
            "
            INSERT INTO token (
                chain_id, address, error
            ) VALUES ($1, $2, $3)

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Read the wrapped `{e}`: 'value too long' → truncate/validate name and symbol against column limits before insert
  2. Use ON CONFLICT (address) DO UPDATE or dedupe tokens before upsert for replay safety
  3. Run migrations to ensure the token table matches the code's expected schema
  4. Confirm DB connectivity and INSERT privileges for the configured role

Example fix

// before
.bind(token.name.as_str()) // may exceed VARCHAR(64)
// after
.bind(token.name.chars().take(64).collect::<String>())
Defensive patterns

Strategy: validation

Validate before calling

const MAX_NAME: usize = 64; // match the column limit
anyhow::ensure!(!token.address.is_empty(), "token address empty");
anyhow::ensure!(token.name.chars().count() <= MAX_NAME && token.symbol.chars().count() <= 32, "token metadata exceeds column length");

Type guard

fn token_valid(t: &Token) -> bool {
    !t.address.is_empty()
        && t.name.chars().count() <= 64
        && t.symbol.chars().count() <= 32
}

Try / catch

if let Err(e) = db.insert_token(&token).await {
    if e.to_string().contains("value too long") {
        let mut t = token.clone(); t.name.truncate(64);
        db.insert_token(&t).await?;
    } else { return Err(e); }
}

Prevention

When it happens

Trigger: Calling the token insert when `token.name` or `token.symbol` exceed column length limits (VARCHAR overflow), `decimals` doesn't fit i32 (it is cast with i32::from(u8) so unlikely), the token address violates a unique constraint/cast, or the pool connection fails during execute.

Common situations: On-chain token metadata with very long names/symbols overflowing VARCHAR(n); replaying token discovery duplicating the address key; fresh database missing the token table; DB role without INSERT privilege.

Related errors


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