sigoden/aichat · info · anyhow::Error
Aborted.
Error message
Aborted.
What it means
abortable_run_with_spinner_rx also races against wait_abort_signal; when an abort signal has already been (or becomes) set through the shared AbortSignal — e.g. a previous Ctrl+C or a programmatic abort — it bails with 'Aborted.' (note the period), distinguishing signal-based aborts from a fresh Ctrl+C ('Aborted!').
Solutions
- Create or reset a fresh AbortSignal for a new run instead of reusing an aborted one
- Avoid calling subsequent spinner-wrapped operations after an abort until state is reset
- Check is_ctrlc / abort state before launching follow-up work
Defensive patterns
Strategy: try-catch
Validate before calling
// only start a run when the signal isn't already aborted
if abort_signal.is_aborted() {
// skip or reset the signal before starting the task
} Try / catch
match abortable_run_with_spinner_rx(task, spinner, signal).await {
Err(e) if e.to_string() == "Aborted." => {
eprintln!("operation aborted via abort signal; not a failure of the task itself");
}
other => other?,
} Prevention
- Create a fresh AbortSignal per independent operation instead of reusing aborted ones
- Check/reset abort state before launching follow-up spinner-wrapped work
- Distinguish 'Aborted.' (signal) from 'Aborted!' (fresh Ctrl+C) in scripts
When it happens
Trigger: Starting or running a spinner-wrapped task after the shared abort signal was set by an earlier Ctrl+C or by abort_signal.abort(); wait_abort_signal resolves and the task is abandoned.
Common situations: Reusing the same AbortSignal across sequential operations; pressing Ctrl+C once and observing the next operation abort immediately; a nested abortable task inheriting an aborted signal.
Related errors
AI-assisted analysis of sigoden/aichat@82976d349a (2026-09-09).
Data as JSON: /api/errors/7da8243ccf7663ac.
Report an issue: GitHub.
Appendix: source
Thrown at src/utils/spinner.rs:163
where
F: Future<Output = Result<T>>,
{
if *IS_STDOUT_TERMINAL {
let (done_tx, done_rx) = oneshot::channel();
let run_task = async {
tokio::select! {
ret = task => {
let _ = done_tx.send(());
ret
}
_ = tokio::signal::ctrl_c() => {
abort_signal.set_ctrlc();
let _ = done_tx.send(());
bail!("Aborted!")
},
_ = wait_abort_signal(&abort_signal) => {
let _ = done_tx.send(());
bail!("Aborted.");
},
}
};
let (task_ret, spinner_ret) = tokio::join!(
run_task,
run_abortable_spinner(spinner_rx, done_rx, abort_signal.clone())
);
spinner_ret?;
task_ret
} else {
task.await
}
}
async fn run_abortable_spinner(
mut spinner_rx: UnboundedReceiver<SpinnerEvent>,
mut done_rx: oneshot::Receiver<()>,
abort_signal: AbortSignal,View on GitHub (pinned to 82976d349a)