GitoxideLabs/gitoxide · error

Could not follow all splits after

Error message

Could not follow all splits after {round} rounds, assuming reference cycle

What it means

When extending a reference transaction through splits of symbolic refs (resolving symref chains into concrete ref edits), the algorithm loops up to 5 rounds; if edits remain after round 5 it gives up with WouldBlock: 'Could not follow all splits after {round} rounds, assuming reference cycle'. It indicates symbolic references form a cycle (e.g. HEAD -> a -> b -> a), so no consistent edit set can be derived.

Solutions

  1. Inspect and fix the symbolic refs (`git symbolic-ref` on the chain) to break the cycle
  2. Delete or rewrite the offending symref files in .git/ manually if tooling can't
  3. Validate the repository with `git fsck` and repair or re-clone
  4. Ensure custom `find` implementations terminate and return real targets, not looping ones
Defensive patterns

Strategy: validation

Validate before calling

// detect symref cycles before transacting
fn resolve(head: &gix_ref::Reference, find: impl Fn(&[u8]) -> Option<gix_ref::Reference>) -> usize {
    let mut seen = std::collections::HashSet::new();
    let mut cur = head.clone();
    while let Some(t) = cur.target().try_into().ok().and_then(|n| find(n.as_ref())) {
        if !seen.insert(t.name().as_bstr().to_vec()) { return seen.len(); /* cycle */ }
        cur = t;
    }
    seen.len()
}

Prevention

When it happens

Trigger: `Transaction::pre_process` calling `extend_with_splits_of_symbolic_refs` on a repository whose symbolic references form a loop longer than 5 resolution rounds, or whose `find` callback keeps returning further symref targets.

Common situations: Manually edited or corrupted symbolic refs in .git/; crafted repositories with ref-to-ref cycles; scripts that create symref chains by accident.

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/3b938e844ecf4341. Report an issue: GitHub.

Appendix: source

Thrown at gix-ref/src/transaction/ext.rs:107

                                    log,
                                    LogChange {
                                        message: log.message.clone(),
                                        mode: RefLog::Only,
                                        force_create_reflog: log.force_create_reflog,
                                    },
                                );
                                let next = std::mem::replace(expected, PreviousValue::Any);
                                RefEdit::update_with_log(referent, new.clone(), next, current).with_deref(true)
                            }
                        },
                    ));
                }
            }
            if new_edits.is_empty() {
                break Ok(());
            }
            if round == 5 {
                break Err(std::io::Error::new(
                    std::io::ErrorKind::WouldBlock,
                    format!("Could not follow all splits after {round} rounds, assuming reference cycle"),
                ));
            }
            round += 1;
            first = self.len();

            self.append(&mut new_edits);
        }
    }
}

View on GitHub (pinned to e73179060b)