GitoxideLabs/gitoxide · error

BUG: must consume and drop entry before getting the next one

Error message

BUG: must consume and drop entry before getting the next one

What it means

This panic comes from an assertion in gix_worktree_stream::Stream::next_entry(). The stream keeps the previous Entry's path buffer alive until the caller drops the entry, because Entry borrows internal stream state. Calling next_entry() again while the previous Entry is still alive would create two live borrows sharing the same mutable buffer, so the library panics by design.

Solutions

  1. Ensure the previously returned Entry is dropped before calling next_entry() again (let it go out of scope or call std::mem::drop(entry)).
  2. Restructure the loop so each entry is fully processed and dropped before fetching the next one.
  3. If you need to keep multiple entries, clone the data you need (e.g. the path or bytes) instead of holding the Entry itself.

Example fix

// before
let e1 = stream.next_entry()?;
let e2 = stream.next_entry()?; // panics: e1 still alive
// after
let e1 = stream.next_entry()?;
let path = e1.path().to_owned();
drop(e1);
let e2 = stream.next_entry()?;
Defensive patterns

Strategy: validation

Validate before calling

if self.path_buf.is_some() {
    // previous entry still borrowed: drop it before fetching the next
    drop(prev_entry);
}
let next = stream.next_entry()?;

Prevention

When it happens

Trigger: Calling stream.next_entry() twice without letting the returned Entry be dropped first, e.g. storing the Entry in a variable that is still in scope during the second call, or holding entries in a collection while iterating.

Common situations: Collecting multiple entries into a Vec in a loop; storing a reference into the Entry while advancing; refactoring from iterator-style code where items are kept alive; forgetting that Entry borrows &mut the stream.

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/83c555971a7df289. Report an issue: GitHub.

Appendix: source

Thrown at gix-worktree-stream/src/entry.rs:19

use std::{
    io::{ErrorKind, Read},
    path::PathBuf,
};

use gix_error::ErrorExt;
use gix_object::bstr::BStr;

use crate::{Entry, Stream, protocol};

/// The error returned by [`next_entry()`][Stream::next_entry()].
pub type Error = gix_error::Exn<gix_error::Message>;

impl Stream {
    /// Access the next entry of the stream or `None` if there is nothing more to read.
    pub fn next_entry(&mut self) -> Result<Option<Entry<'_>>, Error> {
        assert!(
            self.path_buf.is_some(),
            "BUG: must consume and drop entry before getting the next one"
        );
        self.extra_entries.take();
        let res = protocol::read_entry_info(
            &mut self.read,
            self.path_buf.as_mut().expect("set while producing an entry"),
        );
        match res {
            Ok((remaining, mode, id)) => {
                if let Some(err) = self.err.lock().take() {
                    return Err(err);
                }
                Ok(Some(Entry {
                    path_buf: self.path_buf.take(),
                    parent: self,
                    id,
                    mode,
                    remaining,
                }))

View on GitHub (pinned to e73179060b)