FuelLabs/fuel-core · error

The override height is zero. The override height should be…

Error message

The override height is zero. The override height should be greater than zero.

What it means

Thrown at node startup by make_database_compatible_with_config (crates/fuel-core/src/service.rs:335). For ConsensusConfig::PoAV2 the node iterates poa.get_all_overrides(); when the stored block's PoA seal at an override height fails verify_consensus against the startup config, it plans a rollback to override_height - 1 via BlockHeight::pred(). An override at height 0 makes pred() return None, and since there is no height before genesis to roll back to, startup aborts with this error.

Solutions

  1. Move the PoAV2 override to a height greater than 0 (e.g., 1) so rollback to height-1 exists
  2. Wipe/regenerate the on-chain database so genesis is re-executed under the new consensus instead of rolled back
  3. If the goal was only new genesis keys, re-execute genesis with the new chain config rather than declaring an override at height 0
  4. Add a config lint that rejects override keys equal to 0 before startup

Example fix

// before (chain config consensus.PoAV2):
//   overrides: { "0": { "signing_keys": [new_keys] } }
// after:
//   overrides: { "1": { "signing_keys": [new_keys] } }
//   (or start with a fresh database so the new keys apply at genesis)
Defensive patterns

Strategy: validation

Validate before calling

fn overrides_rollbackable(poa: &PoAConfig) -> anyhow::Result<()> {
    for h in poa.get_all_overrides().keys() {
        anyhow::ensure!(!h.is_zero(),
            "override at height 0 cannot be rolled back; use height >= 1");
    }
    Ok(())
}
// run before node start, before make_database_compatible_with_config

Try / catch

match NodeProvider::start(&config).await {
    Err(e) if e.to_string().contains("The override height is zero") => {
        // config/DB mismatch at genesis: fix overrides or wipe DB, do not retry
        eprintln!("override at height 0 is unsupported; move it above 0 or re-init the DB");
        return Err(e);
    }
    other => other,
}

Prevention

When it happens

Trigger: A PoAV2 chain config whose overrides map contains key height 0 (or BlockHeight::default()), used against a non-empty on-chain database whose genesis-block PoA seal no longer verifies under the new consensus config (e.g., changed/re-ordered signing keys).

Common situations: Editing PoAV2 signing-key overrides to take effect 'from genesis' while reusing an existing database; writing the first override height as 0 in a custom chain config; rotating compromised consensus keys without re-initializing the DB.

Related errors


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

Appendix: source

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

                            start_up_consensus_config,
                            &header,
                            &poa_seal,
                        );

                        if !block_valid {
                            found_override_height = Some(override_height);
                        }
                    } else {
                        return Err(anyhow::anyhow!(
                            "The consensus at override height {override_height} is not PoA."
                        ));
                    };
                }
            }
        }

        if let Some(override_height) = found_override_height {
            let rollback_height = override_height.pred().ok_or(anyhow::anyhow!(
                "The override height is zero. \
                The override height should be greater than zero."
            ))?;
            tracing::warn!(
                "The consensus at override height {override_height} \
                does not match with the database. \
                Rollbacking the database to the height {rollback_height}"
            );
            combined_database.rollback_to(rollback_height, shutdown_listener)?;
        }

        Ok(())
    }

    fn override_chain_config_if_needed(&self) -> anyhow::Result<()> {
        let chain_config = self.shared.config.snapshot_reader.chain_config();
        let on_chain_view = self.shared.database.on_chain().latest_view()?;
        let chain_config_hash = chain_config.root()?.into();

View on GitHub (pinned to b9d4d170da)