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

  1. Regenerate the snapshot so code, UTXO, state, and balance tables are written atomically at the same block height.
  2. Validate up front that code ids and UTXO ids are the same set (see validation snippet).
  3. If the contract was spent/pruned deliberately, drop its code row too.
  4. 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

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


AI-assisted analysis of FuelLabs/fuel-core@b9d4d170da (2026-08-16). Data as JSON: /api/errors/47eef8f11a63713f. Report an issue: GitHub.