GitoxideLabs/gitoxide · error

BUG: this method may only be called until it is successful

Error message

BUG: this method may only be called until it is successful

What it means

main_worktree_inner() asserts self.repo is still Some. PrepareCheckout owns the Repository in an Option and removes it once checkout succeeds and the repository is handed to the caller. Calling this method after a successful completion (or after persist/Drop consumed it) violates the type's state machine and panics.

Solutions

  1. Call main_worktree() exactly once; consume the returned Repository and never reuse the PrepareCheckout value.
  2. Recreate the PrepareCheckout instance (via Clone::prepare_fetch/clone) if you need to retry checkout.
  3. If you need the repository after success, use the Repository returned from the successful call.

Example fix

// before
let repo1 = prepare.main_worktree(...)?;
let repo2 = prepare.main_worktree(...)?; // panics: already consumed
// after
let repo = prepare.main_worktree(...)?;
Defensive patterns

Strategy: type-guard

Validate before calling

// PrepareCheckout is crate-internal here; at the call site ensure main_worktree() is invoked once:
assert!(!consumed, "PrepareCheckout already completed");

Prevention

When it happens

Trigger: Calling main_worktree()/main_worktree_inner() a second time on the same PrepareCheckout value after a previous call succeeded and moved the repo out.

Common situations: Retry loops that re-invoke prepare without rebuilding it; code paths that continue after the value was consumed; misuse of the internal (crate-private) API during refactoring.

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/1c408122c7015cac. Report an issue: GitHub.

Appendix: source

Thrown at gix/src/clone/checkout.rs:94

            should_interrupt: &AtomicBool,
        ) -> Result<(Repository, gix_worktree_state::checkout::Outcome), Error>
        where
            P: gix_features::progress::NestedProgress,
            P::SubProgress: gix_features::progress::NestedProgress + 'static,
        {
            self.main_worktree_inner(&mut progress, should_interrupt)
        }

        fn main_worktree_inner(
            &mut self,
            progress: &mut dyn gix_features::progress::DynNestedProgress,
            should_interrupt: &AtomicBool,
        ) -> Result<(Repository, gix_worktree_state::checkout::Outcome), Error> {
            let _span = gix_trace::coarse!("gix::clone::PrepareCheckout::main_worktree()");
            let repo = self
                .repo
                .as_ref()
                .expect("BUG: this method may only be called until it is successful");
            let workdir = repo.workdir().ok_or_else(|| Error::BareRepository {
                git_dir: repo.git_dir().to_owned(),
            })?;

            let root_tree_id = match &self.ref_name {
                Some(reference_val) => Some(repo.find_reference(reference_val)?.peel_to_id()?),
                None => repo.head()?.try_peel_to_id()?,
            };

            let root_tree = match root_tree_id {
                Some(id) => id.object().expect("downloaded from remote").peel_to_tree()?.id,
                None => {
                    return Ok((
                        self.repo.take().expect("still present"),
                        gix_worktree_state::checkout::Outcome::default(),
                    ));
                }
            };

View on GitHub (pinned to e73179060b)