{"record":{"id":"26ec05252f9071d5","repo":"GitoxideLabs/gitoxide","slug":"cancelled-by-user","errorCode":null,"errorMessage":"Cancelled by user","messagePattern":"Cancelled by user","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"gitoxide-core/src/hours/mod.rs","lineNumber":227,"sourceCode":"                        repo.clone(),\n                        stats_counters.clone().expect(\"counters are set\"),\n                    );\n                    (Some(tx), threads)\n                }\n            } else {\n                Default::default()\n            };\n\n            let mut commit_idx = 0_u32;\n            let mut skipped_merge_commits = 0;\n            const CHUNK_SIZE: usize = 50;\n            let mut chunk = Vec::with_capacity(CHUNK_SIZE);\n            let mut commit_iter = commit_id.ancestors(&repo.objects);\n            let mut is_shallow = false;\n            while let Some(c) = commit_iter.next() {\n                progress.inc();\n                if gix::interrupt::is_triggered() {\n                    bail!(\"Cancelled by user\");\n                }\n                match c {\n                    Ok(c) => {\n                        tx.send((commit_idx, commit_iter.commit_data().to_owned())).ok();\n                        let tree_delta_info = tx_tree_id.as_ref().and_then(|tx| {\n                            let mut parents = c.parent_ids.into_iter();\n                            parents\n                                .next()\n                                .map(|first_parent| (tx, Some(first_parent), c.id.to_owned()))\n                                .filter(|_| {\n                                    if parents.next().is_some() {\n                                        skipped_merge_commits += 1;\n                                        false\n                                    } else {\n                                        true\n                                    }\n                                })\n                        });","sourceCodeStart":209,"sourceCodeEnd":245,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gitoxide-core/src/hours/mod.rs#L209-L245","documentation":"In the `hours` command's `estimate`, while walking a commit's ancestors the inner loop checks `gix::interrupt::is_triggered()` after each iteration and bails with this message so a long-running history walk can be cancelled cleanly by SIGINT/Ctrl-C.","triggerScenarios":"Sending SIGINT (Ctrl-C) while `estimate` is traversing commit ancestors via `commit_id.ancestors(&repo.objects)`; the check fires at the top of the next `while let Some(c) = commit_iter.next()` iteration.","commonSituations":"A developer running `gix hours` on a huge repository (tens of thousands of commits) decides to cancel it; a CI pipeline cancellation sends SIGINT mid-walk.","solutions":["Expected on interruption — simply re-run `gix hours` when ready.","Narrow the analysis (smaller repo range, fewer repos) so runs complete faster.","Avoid sending SIGINT during the walk; use progress output to gauge remaining time.","In scripts, treat this error as a cancellation status rather than a bug."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match res {\n    Err(e) if e.to_string() == \"Cancelled by user\" => {\n        eprintln!(\"hours estimate cancelled by SIGINT\");\n    }\n    other => other?,\n}","preventionTips":["Run `gix hours` in a terminal you won't need to interrupt; estimate duration from progress output first.","For very large repositories, sample fewer commits or repos.","In CI, disable automatic cancellation signals for this step or catch them."],"tags":["interrupt","sigint","cancellation","git-history"],"backgroundTag":"user-interrupt","analyzedSha":"e73179060badf27222d790981fac3f84c1830a7e","analyzedAt":"2026-09-08T11:26:50.865Z","contentChangedAt":"2026-09-08T11:26:50.865Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}