nautechsystems/nautilus_trader · error · anyhow::Error

Invalid block timestamp '{block_timestamp}': {e}

Error message

Invalid block timestamp '{block_timestamp}': {e}

What it means

The snapshot row's `block_timestamp` string exists but could not be parsed into a timestamp by `parse_cached_block_timestamp`. The library stores timestamps as strings in a canonical format; any deviation (wrong format, garbage, empty string) makes the cached snapshot unusable because the block time cannot be reconstructed.

Source

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

                u32::try_from(log_index).with_context(|| "Pool snapshot log index is negative")?;
            let block_hash = if transaction_index == BLOCK_SCOPED_SNAPSHOT_INDEX
                && log_index == BLOCK_SCOPED_SNAPSHOT_INDEX
            {
                Some(transaction_hash.clone())
            } else {
                observed_block_hash
            };
            let block_timestamp = row
                .try_get::<Option<String>, _>("block_timestamp")?
                .ok_or_else(|| {
                    anyhow::anyhow!(
                        "Missing block timestamp for pool snapshot {} at block {}",
                        pool_identifier,
                        block
                    )
                })?;
            let timestamp = parse_cached_block_timestamp(&block_timestamp)
                .map_err(|e| anyhow::anyhow!("Invalid block timestamp '{block_timestamp}': {e}"))?;

            let block_position =
                BlockPosition::new(block, transaction_hash, transaction_index, log_index)
                    .with_block_hash(block_hash);

            let fee_protocol_value = row.get::<i16, _>("fee_protocol");
            let fee_protocol = u8::try_from(fee_protocol_value).with_context(|| {
                format!("Invalid pool snapshot fee protocol {fee_protocol_value}")
            })?;
            let fee_protocol0_basis_points = row
                .get::<Option<i32>, _>("fee_protocol0_basis_points")
                .map(|value| {
                    u32::try_from(value).with_context(|| {
                        format!("Invalid token0 fee protocol basis points {value}")
                    })
                })
                .transpose()?;
            let fee_protocol1_basis_points = row

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Check the exact string in the offending row and rewrite it in the canonical format expected by `parse_cached_block_timestamp`.
  2. Re-snapshot the affected pools so rows are rewritten by the current adapter version.
  3. Standardize the writer: confirm the persist path formats timestamps the same way the parser expects (round-trip test).
  4. Normalize legacy rows with an UPDATE converting the old format to the canonical one.

Example fix

// before: writing epoch string
row.bind("block_timestamp", block.timestamp.to_string());
// after: write canonical parseable format
row.bind("block_timestamp", chrono::DateTime::from_timestamp(block.timestamp as i64, 0)
    .unwrap().to_rfc3339());
Defensive patterns

Strategy: validation

Validate before calling

let ts: String = row_ts;
let parsed = chrono::DateTime::parse_from_rfc3339(&ts);
if parsed.is_err() {
    // normalize or reject before loading
    eprintln!("non-canonical block_timestamp: {ts}");
}

Type guard

fn is_canonical_timestamp(s: &str) -> bool {
    chrono::DateTime::parse_from_rfc3339(s).is_ok()
}

Try / catch

match load_snapshot(...).await {
    Err(e) if e.to_string().contains("Invalid block timestamp") => {
        re_snapshot_pool(pool_id).await // rewrite the row via the writer
    }
    other => other,
}

Prevention

When it happens

Trigger: A `block_timestamp` value stored in a format different from what `parse_cached_block_timestamp` expects (e.g. epoch integer string vs ISO-8601, or a locale-dependent format), written by a different adapter version or an external tool that populated the column.

Common situations: Mixed adapter versions writing the same table with different timestamp formats; manual row edits; an ETL/backfill script that inserted Unix epoch seconds where RFC3339 was expected; timezone formatting differences.

Related errors


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