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
- Dedupe the genesis state: find coins sharing the same tx_id and output_index and remove the duplicates, then re-run the import.
- 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.
- Verify the chain id in the node config matches the genesis you intend to run; a mismatch causes import over pre-existing state.
- 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
- Generate genesis snapshots with official tooling instead of hand-editing JSON.
- Run a uniqueness lint over all state tables (coins, messages, contracts, blobs) before import.
- Start a fresh chain against an empty db directory; wipe leftovers between CI runs.
- Pin one canonical genesis file per network and checksum it.
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
- Contract utxo should not exist
- Blob should not exist
- Contract code should not exist
- contract tx_pointer cannot be greater than genesis block
- Message should not exist
AI-assisted analysis of FuelLabs/fuel-core@b9d4d170da (2026-08-16).
Data as JSON: /api/errors/4c5152873ca6dfe4.
Report an issue: GitHub.