bevyengine/bevy · critical
Failed to catch panic!
Error message
Failed to catch panic!
What it means
Inside TaskPool::scope's result collection (task_pool.rs:415-435), each spawned task is awaited; the code expects awaiting a finished task to yield Some(catch_unwind result) and panics with 'Failed to catch panic!' when task.await returns None — i.e. the task completed without producing a result (future dropped/cancelled without being polled to completion or the panic payload was lost). This is an internal invariant of the scope executor, not an application-level error.
Source
Thrown at crates/bevy_tasks/src/task_pool.rs:430
// SAFETY: As above, all futures must complete in this function so we can change the lifetime
let scope: &'env Scope<'_, 'env, T> = unsafe { mem::transmute(&scope) };
f(scope);
if spawned.is_empty() {
Vec::new()
} else {
block_on(async move {
let get_results = async {
let mut results = Vec::with_capacity(spawned.len());
while let Ok(task) = spawned.pop() {
if let Some(res) = task.await {
match res {
Ok(res) => results.push(res),
Err(payload) => std::panic::resume_unwind(payload),
}
} else {
panic!("Failed to catch panic!");
}
}
results
};
let tick_task_pool_executor = tick_task_pool_executor || self.threads.is_empty();
// we get this from a thread local so we should always be on the scope executors thread.
// note: it is possible `scope_executor` and `external_executor` is the same executor,
// in that case, we should only tick one of them, otherwise, it may cause deadlock.
let scope_ticker = scope_executor.ticker().unwrap();
let external_ticker = if !external_executor.is_same(scope_executor) {
external_executor.ticker()
} else {
None
};
match (external_ticker, tick_task_pool_executor) {View on GitHub (pinned to 396ca72708)
Solutions
- Ensure every closure/future given to TaskPool::scope runs to completion (no early task drop)
- Avoid cancelling or abandoning tasks spawned inside a scope
- Update Bevy — this path indicates an executor invariant broke; report with a minimal repro
Defensive patterns
Strategy: validation
Prevention
- Let every future inside TaskPool::scope run to completion; never drop or abandon scoped tasks
- Do not wrap bevy scope tasks in external executors that can cancel them
When it happens
Trigger: Futures passed to scope that get dropped before completion; mixing executors or nesting scopes in ways that cancel tasks; engine bugs in the executor shutdown path.
Common situations: Rare; occasionally surfaces during app shutdown while scoped tasks are still queued, or with custom executor wiring around bevy_tasks.
Related errors
- Failed to spawn thread.
- Task thread panicked while executing.
- Pipeline has not been compiled yet. It is still in the 'Queu
- Pipeline has not been compiled yet. It is still in the 'Crea
- Cannot call `ReflectComponent::reflect_mut` on component {na
AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20).
Data as JSON: /api/errors/37b76e7c94c4b786.
Report an issue: GitHub.