GitoxideLabs/gitoxide · info · anyhow::Error

Cancelled by user

Error message

Cancelled by user

What it means

During pack creation, the object-counting/writing pipeline is wrapped with gix::interrupt::Iter and uses a cancellation closure returning 'Cancelled by user'. When the process interrupt flag is set mid-pack (during traversal or object iteration), the iteration aborts with this message. It is intentional cooperative cancellation of a long-running pack write.

Solutions

  1. Expected behavior: re-run the pack creation command; partial output should be discarded.
  2. Handle this error as a clean-cancel in callers instead of reporting a fault.
  3. For long packs, avoid wrapping interrupt checks or run in an environment that won't signal (detached, nohup).
  4. If cancelled spuriously, ensure only one component initializes the interrupt handler and that flags are reset between runs.

Example fix

// before
let pack = pack::create(&repo, tips, None, &mut progress)?;
// after
let pack = match pack::create(&repo, tips, None, &mut progress) {
    Err(err) if err.to_string() == "Cancelled by user" => return Ok(()),
    other => other?,
};
Defensive patterns

Strategy: try-catch

Try / catch

match pack::create(&repo, tips, input, &mut progress) {
    Err(err) if err.to_string() == "Cancelled by user" => return Ok(()),
    other => other?,
}

Prevention

When it happens

Trigger: Calling gitoxide_core::pack::create (public entry) with an input tip iterator and pressing Ctrl-C during commit traversal or object emission; the interrupt-aware iterator yields the error produced by make_cancellation_err.

Common situations: User aborting a large pack write; CI sending SIGINT/SIGTERM on timeout; interrupt flag set earlier in the process reused here.

Related errors


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

Appendix: source

Thrown at gitoxide-core/src/pack/create.rs:116

        statistics,
        pack_cache_size_in_bytes,
        object_cache_size_in_bytes,
        mut out,
    }: Context<W>,
) -> anyhow::Result<()>
where
    W: std::io::Write,
    P: NestedProgress,
    P::SubProgress: 'static,
{
    type ObjectIdIter = dyn Iterator<Item = Result<ObjectId, Box<dyn std::error::Error + Send + Sync>>> + Send;

    let repo = gix::discover(repository_path)?;
    let pack_compression = repo.pack_compression()?;
    let repo = repo.into_sync();
    progress.init(Some(2), progress::steps());
    let tips = tips.into_iter();
    let make_cancellation_err = || anyhow!("Cancelled by user");
    let (mut handle, mut input): (_, Box<ObjectIdIter>) = match input {
        None => {
            let mut progress = progress.add_child("traversing");
            progress.init(None, progress::count("commits"));
            let tips = tips
                .map({
                    let easy = repo.to_thread_local();
                    move |tip| {
                        ObjectId::from_hex(&Vec::from_os_str_lossy(tip.as_ref())).or_else(|_| {
                            easy.find_reference(tip.as_ref())
                                .map_err(anyhow::Error::from)
                                .and_then(|r| r.into_fully_peeled_id().map(gix::Id::detach).map_err(Into::into))
                        })
                    }
                })
                .collect::<Result<Vec<_>, _>>()?;
            let handle = repo.objects.into_shared_arc().to_cache_arc();
            let iter = Box::new(

View on GitHub (pinned to e73179060b)