{"record":{"id":"be08e5565d3946a4","repo":"GitoxideLabs/gitoxide","slug":"in-restore-mode-a-hash-is-set","errorCode":null,"errorMessage":"in restore mode a hash is set","messagePattern":"in restore mode a hash is set","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"gix-pack/src/data/input/bytes_to_entries.rs","lineNumber":190,"sourceCode":"        Ok(if self.objects_left == 0 {\n            let mut id = gix_hash::ObjectId::null(self.object_hash);\n            if let Err(err) = self.read.read_exact(id.as_mut_slice())\n                && self.mode != input::Mode::Restore\n            {\n                return Err(input::Error::Io(err.into()));\n            }\n\n            if let Some(hash) = self.hash.take() {\n                let actual_id = hash.try_finalize().map_err(gix_hash::io::Error::from)?;\n                if self.mode == input::Mode::Restore {\n                    id = actual_id;\n                } else {\n                    actual_id.verify(&id)?;\n                }\n            }\n            Some(id)\n        } else if self.mode == input::Mode::Restore {\n            let hash = self.hash.clone().expect(\"in restore mode a hash is set\");\n            Some(hash.try_finalize().map_err(gix_hash::io::Error::from)?)\n        } else {\n            None\n        })\n    }\n}\n\nfn read_and_pass_to<R: io::Read, W: io::Write>(read: &mut R, to: W) -> PassThrough<&mut R, W> {\n    PassThrough { read, write: to }\n}\n\nimpl<R> Iterator for BytesToEntriesIter<R>\nwhere\n    R: io::BufRead,\n{\n    type Item = Result<input::Entry, input::Error>;\n\n    fn next(&mut self) -> Option<Self::Item> {","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gix-pack/src/data/input/bytes_to_entries.rs#L172-L208","documentation":"A `expect()` panic in `try_read_trailer` of the pack entry iterator. In `input::Mode::Restore` the iterator must have been constructed with a hash to restore the pack trailer; `self.hash` is `None`, meaning the mode/hash pairing invariant was violated at construction time.","triggerScenarios":"Using `gix_pack::data::input::BytesToEntriesIterator` (or `iterate_from`) with `input::Mode::Restore` where the hash was not supplied at construction — typically only via misusing internal constructors, not the public API which requires the hash for Restore mode.","commonSituations":"Writing custom pack streaming code that copies gix internals, or upgrading across versions where Mode constructors changed.","solutions":["Always supply a `gix_hash::Kind`-derived hash when using `Mode::Restore`; use `Mode::Verify` or `Mode::Ignore` otherwise","Construct iterators through the public `iterate_from`/`new` helpers instead of internal fields","Update gix-pack and adapt to the current Mode API"],"exampleFix":"// before\nlet iter = BytesToEntriesIterator::new(data, Mode::Restore, None, path, id);\n// after\nlet iter = BytesToEntriesIterator::new(data, Mode::Restore, Some(gix_hash::Kind::Sha1), path, id);","handlingStrategy":"validation","validationCode":"// when constructing the iterator, only pass Mode::Restore together with a hash\nlet hash = if mode == input::Mode::Restore { Some(hash_kind) } else { None };","typeGuard":null,"tryCatchPattern":"let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| iterator.next()));\nif r.is_err() { eprintln!(\"Restore mode without hash\"); }","preventionTips":["Always pair Mode::Restore with a supplied hash kind","Prefer public constructors (`iterate_from`) over building the iterator manually","Use Mode::Verify or Mode::Ignore when no hash is available"],"tags":["rust","panic","internal-invariant","gix-pack","pack-streaming"],"backgroundTag":"internal-invariant-violation","analyzedSha":"e73179060badf27222d790981fac3f84c1830a7e","analyzedAt":"2026-09-08T11:26:50.865Z","contentChangedAt":"2026-09-08T11:26:50.865Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}