FuelLabs/fuel-core · error · anyhow::Error

Coin should not exist

Error message

Coin should not exist

What it means

Thrown by init_coin during on-chain genesis import (crates/fuel-core/src/service/genesis/importer/on_chain.rs). Each coin from the genesis state is written via StorageTransaction::replace(&utxo_id, &compressed_coin), and replace returns Some when the key already had a value. Seeing a previous value means this UTXO id was already inserted — either earlier in the same genesis batch or already present in the database — so the importer aborts to keep genesis collision-free and deterministic.

Source

Thrown at crates/fuel-core/src/service/genesis/importer/on_chain.rs:243

        asset_id: *coin.value.asset_id(),
        tx_pointer: *coin.value.tx_pointer(),
    }
    .compress();

    // ensure coin can't point to blocks in the future
    let coin_height = coin.value.tx_pointer().block_height();
    if coin_height > height {
        return Err(anyhow!(
            "coin tx_pointer height ({coin_height}) cannot be greater than genesis block ({height})"
        ));
    }

    if transaction
        .storage::<Coins>()
        .replace(&utxo_id, &compressed_coin)?
        .is_some()
    {
        return Err(anyhow!("Coin should not exist"));
    }

    Ok(())
}

fn init_contract_latest_utxo(
    transaction: &mut StorageTransaction<&mut GenesisDatabase>,
    entry: &TableEntry<ContractsLatestUtxo>,
    height: BlockHeight,
) -> anyhow::Result<()> {
    let contract_id = entry.key;

    if entry.value.tx_pointer().block_height() > height {
        return Err(anyhow!(
            "contract tx_pointer cannot be greater than genesis block"
        ));
    }

View on GitHub (pinned to b9d4d170da)

Solutions

  1. Dedupe the genesis state: find coins sharing the same tx_id and output_index and remove the duplicates, then re-run the import.
  2. If the database is dirty from an earlier run, wipe the configured db directory and start the node again so genesis imports into empty storage.
  3. Verify the chain id in the node config matches the genesis you intend to run; a mismatch causes import over pre-existing state.
  4. Regenerate the state snapshot with the official snapshot tooling and import the fresh output.

Example fix

// before: genesis state lists the same coin twice (same tx_id + output_index)
"coins": [
  { "owner": "0x...", "amount": "1000", "asset_id": "0x...", "tx_pointer": { "block_height": 0, "tx_index": 0 }, "output_index": 0, "tx_id": "0xaaaa..." },
  { "owner": "0x...", "amount": "1000", "asset_id": "0x...", "tx_pointer": { "block_height": 0, "tx_index": 0 }, "output_index": 0, "tx_id": "0xaaaa..." }
]

// after: the (tx_id, output_index) pair appears exactly once
"coins": [
  { "owner": "0x...", "amount": "1000", "asset_id": "0x...", "tx_pointer": { "block_height": 0, "tx_index": 0 }, "output_index": 0, "tx_id": "0xaaaa..." }
]
Defensive patterns

Strategy: validation

Validate before calling

use std::collections::HashSet;

fn validate_genesis_coins(coins: &[fuel_core::chain_config::CoinConfig]) -> Result<(), String> {
    let mut seen = HashSet::new();
    for coin in coins {
        if !seen.insert((coin.tx_id, coin.output_index)) {
            return Err(format!(
                "duplicate coin utxo_id: tx {:?} output {}",
                coin.tx_id, coin.output_index
            ));
        }
    }
    Ok(())
}

Try / catch

match genesis_importer.run().await {
    Err(e) if e.to_string().contains("Coin should not exist") => {
        // report duplicate utxo_id in the genesis state; do not retry with the same input
    }
    rest => rest,
}

Prevention

When it happens

Trigger: The genesis state contains two coins with the same utxo_id (same tx id plus output index), or the node re-imports genesis into a database that already holds those coins because the db directory was left populated from a previous run with the same chain id.

Common situations: Hand-edited or machine-generated genesis JSON with duplicated coin entries; state snapshots regenerated with overlapping ranges; restarting a node against a dirty on-chain db directory; tests building a genesis::Config programmatically and pushing the same coin twice.

Related errors


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