GitoxideLabs/gitoxide · error

time-travel worker panicked

Error message

time-travel worker panicked

What it means

After the time-travel animation finishes, the main thread calls `worker.join()`; if the JoinHandle returns `Err` (the worker thread panicked), the animation wrapper converts it to this error instead of propagating the panic payload.

Solutions

  1. Find the underlying panic: capture the JoinError payload (`err.into_panic()`) and downcast to `&str`/`String` to log the real message and backtrace.
  2. Fix the panicking code in the worker (replace unwraps with error propagation).
  3. Optionally run the worker body under `std::panic::catch_unwind` and report the panic as a normal error event over the channel.

Example fix

// before
if worker.join().is_err() {
    return Err(anyhow::anyhow!("time-travel worker panicked"));
}
// after
if let Err(join_err) = worker.join() {
    let msg = join_err.downcast_ref::<String>().cloned()
        .or_else(|| join_err.downcast_ref::<&str>().map(|s| s.to_string()))
        .unwrap_or_else(|| "unknown panic".into());
    return Err(anyhow::anyhow!("time-travel worker panicked: {msg}"));
}
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-flight: run the worker logic on a sample repo to surface panics early
worker_smoke_test(&repo).expect("time-travel worker logic should not panic");

Type guard

fn panic_message(e: std::boxed::Box<dyn std::any::Any + Send>) -> String {
    e.downcast_ref::<String>().cloned()
        .or_else(|| e.downcast_ref::<&str>().map(|s| s.to_string()))
        .unwrap_or_else(|| "unknown panic".into())
}

Try / catch

match worker.join() {
    Ok(v) => v,
    Err(payload) => return Err(anyhow::anyhow!("time-travel worker panicked: {}", panic_message(payload))),
}

Prevention

When it happens

Trigger: `worker.join()` returns `Err(_)` — the time-travel render/rebase worker thread panicked (e.g. an unwrap/index panic in the animation or rebase-plan code running on that thread).

Common situations: A panic inside worker-side rendering code (bad frame math), object lookups that panic on missing objects, or any `unwrap()` in the worker body triggered by unusual repository state during time-travel animation.

Related errors


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

Appendix: source

Thrown at gix-tix/src/lib.rs:5790

                    std::thread::sleep(FRAME_INTERVAL.saturating_sub(last_draw.elapsed()));
                }
                let id = latest.expect("a changed rebased commit is available");
                if let Err(err) = render(id) {
                    render_error = Some(err);
                    continue;
                }
                rendered = latest;
                last_draw = Some(Instant::now());
            }
            if let Some(result) = complete.take() {
                break result;
            }
        };
        if let Some(err) = render_error {
            tracing::warn!(error = %err, "time-travel animation stopped");
        }
        if worker.join().is_err() {
            return Err(anyhow::anyhow!("time-travel worker panicked"));
        }
        result
    })
}

fn run_rebase_plan(
    terminal: &mut ratatui::DefaultTerminal,
    repository: gix::ThreadSafeRepository,
    graph: &HistoryGraph,
    plan: edit::rebase::Plan,
) -> Result<edit::rebase::PlanPerform> {
    run_with_todo_progress(terminal, move |report| {
        let mut repository = repository.to_thread_local();
        repository.object_cache_size(None);
        edit::rebase::perform_plan_with_progress(&repository, graph, plan, report)
    })
}

View on GitHub (pinned to e73179060b)