{"record":{"id":"09e5a52727b1964c","repo":"FuelLabs/fuel-core","slug":"database-doesn-t-have-a-height-to-rollback","errorCode":null,"errorMessage":"Database doesn't have a height to rollback","messagePattern":"Database doesn't have a height to rollback","errorType":"exception","errorClass":"StorageError","httpStatus":null,"severity":"error","filePath":"crates/fuel-core/src/database.rs","lineNumber":345,"sourceCode":"                    columns_policy: ColumnsPolicy::Lazy,\n                },\n            )\n            .expect(\"Failed to create a temporary database\")\n        }\n    }\n}\n\nimpl<Description> Database<Description>\nwhere\n    Description: DatabaseDescription,\n{\n    pub fn rollback_last_block(&self) -> StorageResult<()> {\n        let mut lock = self.inner_storage().stage.height.lock();\n        let height = *lock;\n\n        let Some(height) = height else {\n            return Err(\n                anyhow::anyhow!(\"Database doesn't have a height to rollback\").into(),\n            );\n        };\n        self.inner_storage().data.rollback_block_to(&height)?;\n        let new_height = height.rollback_height();\n        *lock = new_height;\n        tracing::info!(\n            \"Rollback of the {} to the height {:?} was successful\",\n            Description::name(),\n            new_height\n        );\n\n        Ok(())\n    }\n\n    fn latest_view_with_height(\n        &self,\n        height: Option<Description::Height>,\n    ) -> StorageResult<IterableKeyValueView<ColumnType<Description>, Description::Height>>","sourceCodeStart":327,"sourceCodeEnd":363,"githubUrl":"https://github.com/FuelLabs/fuel-core/blob/b9d4d170da3a31c9ace5f963d633b326348e0d42/crates/fuel-core/src/database.rs#L327-L363","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check latest_height_from_metadata() (or the staged height) for Some before calling rollback_last_block.","Ensure at least genesis is imported/committed before any rollback logic runs.","Bound rollback loops so they stop at the target height instead of exhausting all blocks."],"exampleFix":"// before\ndb.rollback_last_block()?;\n\n// after\nif db.latest_height_from_metadata()?.is_some() {\n    db.rollback_last_block()?;\n}","handlingStrategy":"validation","validationCode":"match db.latest_height_from_metadata()? {\n    Some(_) => db.rollback_last_block()?,\n    None => anyhow::bail!(\"nothing to roll back: database has no committed height\"),\n}","typeGuard":null,"tryCatchPattern":"match db.rollback_last_block() {\n    Ok(()) => Ok(()),\n    Err(e) if e.to_string().contains(\"no a height to rollback\") => {\n        // already at/below genesis — treat as done, not as failure\n        Ok(())\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Bound rollback loops at an explicit target height; never loop unbounded on rollback_last_block.","Check for a committed height (genesis included) before rolling back.","In tests, always import a block before exercising rollback paths."],"tags":["database","rollback","initialization","rust"],"backgroundTag":null,"analyzedSha":"b9d4d170da3a31c9ace5f963d633b326348e0d42","analyzedAt":"2026-08-16T08:56:42.692Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}