FuelLabs/fuel-core · error · anyhow::Error
Missing utxo for contract: {id}
Error message
Missing utxo for contract: {id} What it means
The chain-config state builder requires every contract that has bytecode to also carry a UTXO record (utxo_id + tx_pointer from ContractUtxoInfo::V1). When contract_code contains an id missing from contract_utxos, conversion fails with 'Missing utxo for contract: {id}' (crates/chain-config/src/config/state.rs:224-227). The code check runs first, so this error specifically means the bytecode was snapshotted but the contract's UTXO entry was not — again an inconsistent snapshot.
Source
Thrown at crates/chain-config/src/config/state.rs:227
.contract_utxo
.into_iter()
.map(|entry| match entry.value {
ContractUtxoInfo::V1(utxo) => {
(entry.key, (utxo.utxo_id, utxo.tx_pointer))
}
_ => unreachable!(),
})
.collect();
let contracts = contract_ids
.into_iter()
.map(|id| -> anyhow::Result<_> {
let code = contract_code
.remove(&id)
.ok_or_else(|| anyhow::anyhow!("Missing code for contract: {id}"))?;
let (utxo_id, tx_pointer) = contract_utxos
.remove(&id)
.ok_or_else(|| anyhow::anyhow!("Missing utxo for contract: {id}"))?;
let states = state.remove(&id).unwrap_or_default();
let balances = balance.remove(&id).unwrap_or_default();
Ok(ContractConfig {
contract_id: id,
code,
tx_id: *utxo_id.tx_id(),
output_index: utxo_id.output_index(),
tx_pointer_block_height: tx_pointer.block_height(),
tx_pointer_tx_idx: tx_pointer.tx_index(),
states,
balances,
})
})
.try_collect()?;
Ok(StateConfig {
coins,View on GitHub (pinned to b9d4d170da)
Solutions
- Regenerate the snapshot so code, UTXO, state, and balance tables are written atomically at the same block height.
- Validate up front that code ids and UTXO ids are the same set (see validation snippet).
- If the contract was spent/pruned deliberately, drop its code row too.
- Ensure the snapshot's ContractUtxoInfo entries are V1; older/different versions are rejected by this same code path.
Example fix
// before — fails with 'Missing utxo for contract: 0x…'
let state_config = StateConfig::generate(&mut reader)?;
// after — require identical id sets before building the config
let code_ids: HashSet<_> = contract_code.keys().copied().collect();
let utxo_ids: HashSet<_> = contract_utxos.keys().copied().collect();
anyhow::ensure!(
code_ids == utxo_ids,
"snapshot tables inconsistent: code-only {:?}, utxo-only {:?}",
code_ids.difference(&utxo_ids),
utxo_ids.difference(&code_ids)
); Defensive patterns
Strategy: validation
Validate before calling
// Require identical contract id sets across code and UTXO tables before conversion
let code_ids: HashSet<_> = contract_code.keys().copied().collect();
let utxo_ids: HashSet<_> = contract_utxos.keys().copied().collect();
anyhow::ensure!(
code_ids == utxo_ids,
"snapshot tables inconsistent: code-only {:?}, utxo-only {:?}",
code_ids.difference(&utxo_ids),
utxo_ids.difference(&code_ids)
); Try / catch
match build_config(&reader) {
Err(e) if e.to_string().contains("Missing utxo for contract") => {
// bytecode exists but the UTXO record does not → regenerate snapshot
regenerate_snapshot(&node_url).await?;
}
other => other?,
} Prevention
- Export all contract tables from the same node and block height in one pass.
- Ensure snapshot ContractUtxoInfo entries are V1; other variants hit unreachable!() in the same code path.
- Automate the id-set comparison in snapshot tooling so mismatches never reach the config builder.
When it happens
Trigger: Generating the StateConfig where the contract-code table contains a ContractId with no matching ContractUtxoInfo::V1 entry in the UTXO map.
Common situations: UTXO table truncated or pruned independently of the code table; snapshot exported at slightly different heights per table; hand-edited snapshots; versions of snapshot tables whose ContractUtxoInfo format the reader does not map (only V1 is handled — other variants hit unreachable!).
Related errors
- Missing code for contract: {id}
- No block height found
- No config found
- Fragments use different compressions.
- Fragments don't have the same encoding and cannot be merged.
AI-assisted analysis of FuelLabs/fuel-core@b9d4d170da (2026-08-16).
Data as JSON: /api/errors/47eef8f11a63713f.
Report an issue: GitHub.