nautechsystems/nautilus_trader · error · anyhow::Error
Finalized header base fee is invalid
Error message
Finalized header base fee is invalid
What it means
Raised in database.rs:3749 when the nullable base_fee_per_gas TEXT column of a verified finalized header row cannot be parsed as a u128. The library stores numeric header fields as text and validates on read; a value that is not a valid unsigned integer means the persisted header data is corrupt or was written by a foreign writer.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:3749
let (number, hash, parent_hash, timestamp, base_fee, digest) = row;
anyhow::ensure!(
digest == manifest_digest,
"Finalized header manifest identity changed"
);
Ok(Some(ExecutionVerificationPosition {
next_canonical_nonce: u64::try_from(nonce).context("Canonical nonce is negative")?,
revision: u64::try_from(revision).context("Canonical nonce revision is negative")?,
finalized_tip: ExecutionVerifiedHeader {
number: u64::try_from(number).context("Finalized header number is negative")?,
hash,
parent_hash,
timestamp: u64::try_from(timestamp)
.context("Finalized header timestamp is negative")?,
base_fee_per_gas: base_fee
.map(|value| {
value
.parse::<u128>()
.map_err(|_| anyhow::anyhow!("Finalized header base fee is invalid"))
})
.transpose()?,
},
}))
}
pub(crate) async fn load_execution_verified_header(
&self,
chain_id: u32,
wallet_address: &str,
number: u64,
manifest_digest: &str,
) -> anyhow::Result<Option<ExecutionVerifiedHeader>> {
let chain_id =
i32::try_from(chain_id).context("Verification chain ID exceeds PostgreSQL INTEGER")?;
let number =
i64::try_from(number).context("Finalized header number exceeds PostgreSQL BIGINT")?;
let row = sqlx::query_as::<_, (String, String, i64, Option<String>, String)>(View on GitHub (pinned to 18893faf8b)
Solutions
- Inspect the offending row: SELECT number, base_fee_per_gas FROM execution_verified_finalized_header WHERE base_fee_per_gas IS NOT NULL AND base_fee_per_gas !~ '^[0-9]+$';
- Re-record the header via verification bootstrap/re-ingestion from the chain so the base fee is stored as a canonical decimal string.
- Restore the affected rows from a known-good backup if corruption is widespread.
- Do not patch the string by hand; use the writer path so formatting stays consistent.
Example fix
-- before: malformed value UPDATE ... base_fee_per_gas = '12.5 gwei' -- breaks u128 parse -- after: canonical integer string in wei -- base_fee_per_gas = '12500000000'
Defensive patterns
Strategy: validation
Validate before calling
let bad: Vec<(i64, Option<String>)> = sqlx::query_as(
"SELECT number, base_fee_per_gas FROM execution_verified_finalized_header \
WHERE chain_id = $1 AND base_fee_per_gas IS NOT NULL \
AND base_fee_per_gas !~ '^[0-9]+$'",
).bind(chain_id).fetch_all(&pool).await?;
assert!(bad.is_empty(), "malformed base fee rows: {bad:?}"); Try / catch
match load_verified_header(...).await {
Err(e) if e.to_string().contains("Finalized header base fee is invalid") => {
// flag row corrupt; re-ingest the header from the chain
},
other => other?,
} Prevention
- Only write header rows through the library's writer path
- Validate base-fee strings with a regex before bulk imports
- Include base-fee format checks in database integrity tests
When it happens
Trigger: Loading the finalized tip in load_execution_verification_position when base_fee_per_gas is non-null but fails "...".parse::<u128>() (empty string, negative number, decimal, non-numeric text, or a value above u128 range).
Common situations: Manual row edits or data imports that wrote malformed base-fee strings; a bug in an older writer version encoding the fee differently; corrupted rows after a partial restore/migration.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Replacement scan cursor base fee is invalid
- Canonical nonce overflow
- Replacement scan cursor number is negative
- Replacement scan cursor timestamp is negative
- Stored execution payload has a truncated envelope header
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/51913bceea01c35a.
Report an issue: GitHub.