GitoxideLabs/gitoxide · warning

last seen won't lie

Error message

last seen won't lie

What it means

A `expect()` panic in `assert_is_incrementing_and_update_next_offset` of the delta tree cache (`gix_pack::cache::delta::Tree`). `last_seen` being `Some` guarantees the corresponding items vec is non-empty (a node was pushed when last_seen was set); `last_mut()` failing means bookkeeping between `last_seen` and the item vecs desynchronized.

Solutions

  1. Update gix-pack; out-of-order offsets produce a normal error, not this panic
  2. Feed entries in pack-offset order as produced by the pack index
  3. Report a reproducer upstream if triggered
Defensive patterns

Strategy: validation

Validate before calling

// entries must be added in strictly increasing pack offsets
// track previous offset yourself and skip/regress gracefully:
if offset <= last_offset { return Err("non-increasing offset".into()); }

Try / catch

let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| tree.add_child(...)));
if r.is_err() { eprintln!("delta tree state corrupted"); }

Prevention

When it happens

Trigger: Adding pack entries to a `gix_pack::cache::delta::Tree` via `set_pack_entries_end_and_resolve_ref_offsets`, `add_root`, `add_child`, or `add_child_by_id` when internal state was corrupted — practically only via a library defect or misuse of internal mutation APIs.

Common situations: Seen in delta-cache development, fuzzing pack indexes with out-of-order offsets (which normally yields a proper `InvariantIncreasingPackOffset` error instead), or inconsistent gix crate versions.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08). Data as JSON: /api/errors/4e2589091462ae8a. Report an issue: GitHub.

Appendix: source

Thrown at gix-pack/src/cache/delta/tree.rs:92

    }

    /// Returns self's root and child items.
    ///
    /// You can rely on them following the same `children` invariants as they did in the tree
    pub(super) fn take_root_child_and_refs(self) -> (Vec<Item<T>>, Vec<Item<T>>, RefDeltaChildren) {
        (self.root_items, self.child_items, self.ref_child_indices)
    }

    pub(super) fn assert_is_incrementing_and_update_next_offset(
        &mut self,
        offset: crate::data::Offset,
    ) -> Result<(), Error> {
        let items = match &self.last_seen {
            Some(NodeKind::Root) => &mut self.root_items,
            Some(NodeKind::Child) => &mut self.child_items,
            None => return Ok(()),
        };
        let item = &mut items.last_mut().expect("last seen won't lie");
        if offset <= item.offset {
            return Err(Error::InvariantIncreasingPackOffset {
                last_pack_offset: item.offset,
                pack_offset: offset,
            });
        }
        item.next_offset = offset;
        Ok(())
    }

    pub(super) fn set_pack_entries_end_and_resolve_ref_offsets(
        &mut self,
        pack_entries_end: crate::data::Offset,
    ) -> Result<(), traverse::Error> {
        if !self.future_child_offsets.is_empty() {
            for (parent_offset, child_index) in self.future_child_offsets.drain(..) {
                // SAFETY invariants upheld:
                //  - We are draining from future_child_offsets and adding to children, keeping things the same.

View on GitHub (pinned to e73179060b)