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

Off chain database is already initialized

Error message

Off chain database is already initialized

What it means

Companion to the on-chain check: the off-chain database's into_genesis() fails when it already contains indexed state. Both databases must be empty for SnapshotImporter to run; this error names the off-chain side as the initialized one.

Source

Thrown at crates/fuel-core/src/service/genesis.rs:88

/// Performs the importing of the genesis block from the snapshot.
pub async fn execute_genesis_block(
    watcher: StateWatcher,
    config: &Config,
    db: &CombinedDatabase,
) -> anyhow::Result<UncommittedImportResult<Changes>> {
    let genesis_block = create_genesis_block(config);
    tracing::info!("Genesis block created: {:?}", genesis_block.header());
    let on_chain = db
        .on_chain()
        .clone()
        .into_genesis()
        .map_err(|_| anyhow::anyhow!("On chain database is already initialized"))?;
    let off_chain = db
        .off_chain()
        .clone()
        .into_genesis()
        .map_err(|_| anyhow::anyhow!("Off chain database is already initialized"))?;

    let genesis_db = CombinedGenesisDatabase {
        on_chain,
        off_chain,
    };

    SnapshotImporter::import(
        genesis_db.clone(),
        genesis_block.clone(),
        config.snapshot_reader.clone(),
        watcher,
    )
    .await?;

    let genesis_progress_on_chain: Vec<String> = db
        .on_chain()
        .iter_all_keys::<GenesisMetadata<OnChain>>(None)
        .try_collect()?;

View on GitHub (pinned to b9d4d170da)

Solutions

  1. Wipe the entire database directory (on-chain AND off-chain together) before re-running genesis.
  2. If state should be preserved, skip genesis import entirely.
  3. Check that db-path isn't shared between nodes/runs.
Defensive patterns

Strategy: validation

Validate before calling

// Same emptiness probe must cover BOTH databases — wiping only one side
// just moves the failure to the other:
async fn needs_genesis(db: &CombinedDatabase) -> bool {
    on_chain_is_empty(db.on_chain()).await && off_chain_is_empty(db.off_chain()).await
}

Try / catch

if let Err(e) = genesis_result {
    if e.to_string().contains("already initialized") {
        return Err(anyhow!("off-chain (or on-chain) DB not empty: wipe the whole db-path"));
    }
    return Err(e);
}

Prevention

When it happens

Trigger: execute_genesis_block runs while the off-chain DB (balances, tx-index, etc.) at the configured path already holds state from a prior run.

Common situations: Same as the on-chain case: reused data directories, partial prior initializations, CI runs sharing paths.

Related errors


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