a-b-street/abstreet · error · anyhow::Error

channel canceled

Error message

channel canceled

What it means

FileLoaded wraps a channel receiver; in its event handler, try_recv failing means the sender (the fetch worker thread) was dropped or the channel closed unexpectedly instead of delivering Ok/None. The library formats 'channel canceled' and invokes on_load with the error, after logging the underlying TryRecvError.

Solutions

  1. Check the prior 'channel failed: {:?}' log line for the real TryRecvError cause
  2. Look for panics in the fetch callback (e.g. unwrap on send) and handle them gracefully
  3. Ensure the loader is dropped/recreated together with its producer when navigating
  4. Retry the load operation

Example fix

// before
let buffer = array.to_vec();
tx.send(Ok(buffer)).unwrap(); // panic here kills the sender -> 'channel canceled'
// after
if let Err(e) = tx.send(Ok(array.to_vec())) {
    error!("receiver dropped: {:?}", e);
}
Defensive patterns

Strategy: try-catch

Try / catch

match receiver.try_recv() {
    Ok(v) => use(v),
    Err(std::sync::mpsc::TryRecvError::Empty) => { /* keep waiting */ }
    Err(e) => {
        error!("channel failed: {:?}", e);
        on_load(ctx, app, Err(anyhow!("channel canceled: {:?}", e)));
    }
}

Prevention

When it happens

Trigger: The spawning side (thread/future holding the tx sender) panicked or was dropped before sending any result — e.g. tx.send(...).unwrap() panicked inside the fetch callback, or the FileLoader outlived the worker.

Common situations: A panic inside the download callback (bad data, unwrap on a None/Err), the async task being cancelled, or replaying/rewinding such that the loader outlives its producer.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of a-b-street/abstreet@0964f29315 (2026-09-13). Data as JSON: /api/errors/f860f29b0c7e396e. Report an issue: GitHub.

Appendix: source

Thrown at widgetry/src/tools/load.rs:387

            outer_progress_receiver: Some(outer_progress_receiver),
            inner_progress_receiver: Some(inner_progress_receiver),
            last_outer_progress: String::new(),
            last_inner_progress: String::new(),
        })
    }
}

impl<A, T> State<A> for FutureLoader<A, T>
where
    A: 'static,
    T: 'static,
{
    fn event(&mut self, ctx: &mut EventCtx, app: &mut A) -> Transition<A> {
        match self.receiver.try_recv() {
            Err(e) => {
                error!("channel failed: {:?}", e);
                let on_load = self.on_load.take().unwrap();
                on_load(ctx, app, Err(anyhow!("channel canceled")))
            }
            Ok(None) => {
                if let Some(ref mut rx) = self.outer_progress_receiver {
                    // Read all of the progress that's happened
                    loop {
                        match rx.try_next() {
                            Ok(Some(msg)) => {
                                self.last_outer_progress = msg;
                            }
                            Ok(None) => {
                                self.outer_progress_receiver = None;
                                break;
                            }
                            Err(_) => {
                                // No messages
                                break;
                            }
                        }

View on GitHub (pinned to 0964f29315)