GitoxideLabs/gitoxide · error

peeled ref

Error message

peeled ref

What it means

Panic raised by `.expect("peeled ref")` in `follow_to_object_packed`. The function follows symbolic references until it reaches a direct (peeled) target, then calls `target.try_id()`. The invariant says that after following, the target must be a plain object id; if it is still symbolic, `try_id()` returns None and the code panics. This indicates the ref chain did not terminate in a peeled target within `MAX_REF_DEPTH` or a parsing/logic bug.

Solutions

  1. Inspect the packed-refs file for broken or self-referential symbolic entries and repair them (e.g. with `git pack-refs` or manual editing).
  2. Reduce symbolic ref chain depth so it resolves within MAX_REF_DEPTH.
  3. Report the panic to gitoxide if the ref chain is well-formed; this is an internal invariant breach.
Defensive patterns

Strategy: validation

Validate before calling

// detect unresolved symbolic chains in packed-refs before peeling
let r = repo.find_reference(name)?;
if r.is_symbolic() && depth_follow(&r)? >= MAX_DEPTH { return Err("symbolic chain too deep".into()); }

Prevention

When it happens

Trigger: Calling `peel_to_id_packed`, `follow_to_object_in_place_packed`, `to_id_long_jump`, or `to_id_cycle` on a packed reference whose symbolic chain does not resolve to a peeled object id (e.g. extremely long or cyclic symbolic chains that evade the depth-limit check).

Common situations: Reading refs from a packed-refs file containing deeply nested or malformed symbolic ref chains; repositories hand-edited or written by tooling that created dangling symbolic refs in packed-refs.

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/9ee0c596979d485f. Report an issue: GitHub.

Appendix: source

Thrown at gix-ref/src/store/file/raw_ext.rs:225

                let mut seen = BTreeSet::new();
                let cursor = &mut *self;
                while let Some(next) = cursor.follow_packed(store, packed) {
                    let next = next?;
                    if seen.contains(&next.name) {
                        return Err(peel::to_object::Error::Cycle {
                            start_absolute: store.reference_path(cursor.name.as_ref()),
                        });
                    }
                    *cursor = next;
                    seen.insert(cursor.name.clone());
                    const MAX_REF_DEPTH: usize = 5;
                    if seen.len() == MAX_REF_DEPTH {
                        return Err(peel::to_object::Error::DepthLimitExceeded {
                            max_depth: MAX_REF_DEPTH,
                        });
                    }
                }
                let oid = self.target.try_id().expect("peeled ref").to_owned();
                Ok(oid)
            }
        }
    }

    fn follow(&self, store: &file::Store) -> Option<Result<Reference, file::find::existing::Error>> {
        let packed = match store
            .assure_packed_refs_uptodate()
            .map_err(|err| file::find::existing::Error::Find(file::find::Error::PackedOpen(err)))
        {
            Ok(packed) => packed,
            Err(err) => return Some(Err(err)),
        };
        self.follow_packed(store, packed.as_ref().map(|b| &***b))
    }

    fn follow_packed(
        &self,

View on GitHub (pinned to e73179060b)