GitoxideLabs/gitoxide · error

BUG: must not add entries after the start of entries…

Error message

BUG: must not add entries after the start of entries traversal

What it means

Stream::add_entry() panics when extra_entries channel is None, which happens after the first call to next_entry() started entry traversal. Additional entries are delivered via a channel that is only present before traversal begins, so adding entries mid-traversal is an invalid state the library asserts against.

Solutions

  1. Call add_entry() for all additional entries BEFORE the first next_entry() call.
  2. Re-create the Stream if you need to add entries after traversal already started.
  3. Restructure so the set of extra entries is known up front.

Example fix

// before
let e = stream.next_entry()?;
stream.add_entry(extra)?; // panics: traversal started
// after
stream.add_entry(extra)?;
let e = stream.next_entry()?;
Defensive patterns

Strategy: validation

Validate before calling

if stream.has_started_traversal() { /* API-specific guard if available */ }
// In practice: add all entries before the first next_entry() call.

Prevention

When it happens

Trigger: Calling stream.add_entry(...) (directly or via add_entry_from_path) after the first stream.next_entry() call has been made.

Common situations: Building the entry list lazily as you read; adding a late-discovered file after traversal started; confusing ordering of setup code versus iteration code.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at gix-worktree-stream/src/lib.rs:118

            err: Default::default(),
            buf: std::iter::repeat_n(0, u16::MAX as usize).collect(),
            pos: 0,
            filled: 0,
        }
    }
}

/// Entries
impl Stream {
    /// Add `entry` to the list of entries to be returned in calls to [`Self::next_entry()`].
    ///
    /// The entry will be returned after the one contained in the tree, in order of addition.
    /// # Panics
    /// If called after the first call to [`Self::next_entry()`].
    pub fn add_entry(&mut self, entry: AdditionalEntry) -> &mut Self {
        self.extra_entries
            .as_ref()
            .expect("BUG: must not add entries after the start of entries traversal")
            .send(entry)
            .expect("Failure is impossible as thread blocks on the receiving end");
        self
    }

    /// Add the item at `path` as entry to this stream, which is expected to be under `root`.
    ///
    /// Note that the created entries will always have a null hash, and that we access this path
    /// to determine its type, and will access it again when it is requested.
    pub fn add_entry_from_path(
        &mut self,
        root: &Path,
        path: &Path,
        object_hash: gix_hash::Kind,
    ) -> std::io::Result<&mut Self> {
        let rela_path = path.strip_prefix(root).map_err(std::io::Error::other)?;
        let meta = path.symlink_metadata()?;
        let relative_path = gix_path::to_unix_separators_on_windows(gix_path::into_bstr(rela_path)).into_owned();

View on GitHub (pinned to e73179060b)