GitoxideLabs/gitoxide · info · anyhow::Error

Cancelled by user

Error message

Cancelled by user

What it means

The `gix odb statistics` command supports cancellation: when the user signals interruption (Ctrl-C) while iterating all objects, `gix::interrupt::Iter` short-circuits and the code raises `anyhow::anyhow!("Cancelled by user")`. It is a deliberate user-interrupt error, not a library fault.

Solutions

  1. Don't send SIGINT if you need the complete statistics output
  2. Re-run without interruption; the operation is read-only and safe to repeat
  3. Handle this message specially in scripts to distinguish user abort from failure
Defensive patterns

Strategy: try-catch

Try / catch

match cmd_output {
    Err(e) if e.to_string().contains("Cancelled by user") => info!("user aborted statistics"),
    Err(e) => error!("statistics failed: {e}"),
    Ok(stats) => /* ... */,
}

Prevention

When it happens

Trigger: Running `gix odb statistics` (gitoxide-core `repository::odb::statistics`) and sending SIGINT / pressing Ctrl-C while the loose or packed object scan is in progress.

Common situations: Users aborting long-running object counts on huge repositories; automated scripts sending signals on timeout.

Related errors


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

Appendix: source

Thrown at gitoxide-core/src/repository/odb.rs:174

        type Error = anyhow::Error;

        fn feed(&mut self, items: Self::Input) -> Result<Self::FeedProduce, Self::Error> {
            for (id, item) in items? {
                self.stats.consume(item);
                if let Some(ids) = self.stats.ids.as_mut() {
                    ids.push(id);
                }
            }
            Ok(())
        }

        fn finalize(mut self) -> Result<Self::Output, Self::Error> {
            self.stats.total_objects = self.stats.loose_objects + self.stats.packed_objects;
            Ok(self.stats)
        }
    }

    let cancelled = || anyhow::anyhow!("Cancelled by user");
    let object_ids = repo.objects.iter()?.filter_map(Result::ok);
    let chunk_size = 1_000;
    let mut stats = if gix::parallel::num_threads(thread_limit) > 1 {
        gix::parallel::in_parallel(
            gix::interrupt::Iter::new(
                gix::features::iter::Chunks {
                    inner: object_ids,
                    size: chunk_size,
                },
                cancelled,
            ),
            thread_limit,
            {
                let objects = repo.objects.clone();
                move |_| (objects.clone().into_inner(), counter)
            },
            |ids, (handle, counter)| {
                let ids = ids?;

View on GitHub (pinned to e73179060b)