FuelLabs/fuel-core · error · anyhow::Error
Total balance overflow: coins: {coins}, messages: {non_retry
Error message
Total balance overflow: coins: {coins}, messages: {non_retryable} What it means
The GraphQL off-chain balance query adds the owner's base-asset coin total and non-retryable message balance with checked_add on TotalBalanceAmount (u64). Overflow requires the combined value to exceed u64::MAX — a state a sane chain cannot reach — so this is a sanity guard against corrupted storage or absurd genesis amounts, not an expected arithmetic event.
Source
Thrown at crates/fuel-core/src/service/adapters/graphql_api/off_chain.rs:225
base_asset_id: &AssetId,
) -> StorageResult<TotalBalanceAmount> {
let coins = self
.storage_as_ref::<CoinBalances>()
.get(&CoinBalancesKey::new(owner, asset_id))?
.unwrap_or_default()
.into_owned() as TotalBalanceAmount;
if base_asset_id == asset_id {
let MessageBalance {
retryable: _, // TODO: https://github.com/FuelLabs/fuel-core/issues/2448
non_retryable,
} = self
.storage_as_ref::<MessageBalances>()
.get(owner)?
.unwrap_or_default()
.into_owned();
let total = coins.checked_add(non_retryable).ok_or(anyhow::anyhow!(
"Total balance overflow: coins: {coins}, messages: {non_retryable}"
))?;
Ok(total)
} else {
Ok(coins)
}
}
fn balances<'a>(
&'a self,
owner: &Address,
start: Option<AssetId>,
base_asset_id: &'a AssetId,
direction: IterDirection,
) -> BoxedIter<'a, StorageResult<(AssetId, TotalBalanceAmount)>> {
match (direction, start) {
(IterDirection::Forward, None) => {
self.base_asset_first(owner, base_asset_id, direction)View on GitHub (pinned to b9d4d170da)
Solutions
- Inspect the owner's coins and MessageBalances records for max-value/corrupted entries and repair the state.
- When building test chains, keep minted amounts well below u64::MAX.
- If this fires on a healthy chain, treat it as storage corruption and report upstream with the owner's state.
Defensive patterns
Strategy: try-catch
Try / catch
// At the GraphQL/API boundary, surface the guard as a clean query error:
match balance_result {
Ok(total) => Some(total),
Err(e) if e.to_string().contains("Total balance overflow") => {
tracing::error!("corrupt balance state for owner {owner:?}");
None // or a domain-specific GraphQL error
}
Err(e) => return Err(e.into()),
} Prevention
- Validate snapshot amounts at chain-creation time (sums below u64::MAX).
- Treat this error as data corruption — investigate the owner's coins/messages, don't retry.
- In test chains, avoid near-u64::MAX mint values.
When it happens
Trigger: Querying balance(owner, base_asset_id) when stored coin total + non_retryable message balance > u64::MAX — corrupted balances table, hand-crafted genesis with near-max amounts, or test fixtures summing saturated values.
Common situations: Chains created from custom snapshots with miscomputed supply; storage corruption after hardware faults; integration tests that max out balances.
Related errors
- {error}
- Failed to get registry root: {}
- Failed to get registry root: {}
- The `message_block_height` is higher than `commit_block_heig
- The count of leaves - messages is zero
AI-assisted analysis of FuelLabs/fuel-core@b9d4d170da (2026-08-16).
Data as JSON: /api/errors/98c5a395c6bd1d5d.
Report an issue: GitHub.