FuelLabs/fuel-core · error · StorageError

Database doesn't have a height to rollback

Error message

Database doesn't have a height to rollback

What it means

Database::rollback_last_block reads the currently staged height (self.inner_storage().stage.height). If it is None — the database has never committed a block (not even genesis), or the stage was never populated — there is no block to roll back and the operation fails. This is the per-database primitive underlying CombinedDatabase::rollback_to.

Source

Thrown at crates/fuel-core/src/database.rs:345

                    columns_policy: ColumnsPolicy::Lazy,
                },
            )
            .expect("Failed to create a temporary database")
        }
    }
}

impl<Description> Database<Description>
where
    Description: DatabaseDescription,
{
    pub fn rollback_last_block(&self) -> StorageResult<()> {
        let mut lock = self.inner_storage().stage.height.lock();
        let height = *lock;

        let Some(height) = height else {
            return Err(
                anyhow::anyhow!("Database doesn't have a height to rollback").into(),
            );
        };
        self.inner_storage().data.rollback_block_to(&height)?;
        let new_height = height.rollback_height();
        *lock = new_height;
        tracing::info!(
            "Rollback of the {} to the height {:?} was successful",
            Description::name(),
            new_height
        );

        Ok(())
    }

    fn latest_view_with_height(
        &self,
        height: Option<Description::Height>,
    ) -> StorageResult<IterableKeyValueView<ColumnType<Description>, Description::Height>>

View on GitHub (pinned to b9d4d170da)

Solutions

  1. Check latest_height_from_metadata() (or the staged height) for Some before calling rollback_last_block.
  2. Ensure at least genesis is imported/committed before any rollback logic runs.
  3. Bound rollback loops so they stop at the target height instead of exhausting all blocks.

Example fix

// before
db.rollback_last_block()?;

// after
if db.latest_height_from_metadata()?.is_some() {
    db.rollback_last_block()?;
}
Defensive patterns

Strategy: validation

Validate before calling

match db.latest_height_from_metadata()? {
    Some(_) => db.rollback_last_block()?,
    None => anyhow::bail!("nothing to roll back: database has no committed height"),
}

Try / catch

match db.rollback_last_block() {
    Ok(()) => Ok(()),
    Err(e) if e.to_string().contains("no a height to rollback") => {
        // already at/below genesis — treat as done, not as failure
        Ok(())
    }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: Calling rollback_last_block on a database with no committed height: freshly created DB, DB already rolled back to/below genesis, or a DB whose stage was reset.

Common situations: Generic rollback tooling invoked on an empty chain; rollback loops that run one iteration too many (rolling back past the first block); tests calling rollback_last_block without importing a block first.

Related errors


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