{"record":{"id":"49823738f0d7bd78","repo":"bevyengine/bevy","slug":"task-thread-panicked-while-executing","errorCode":null,"errorMessage":"Task thread panicked while executing.","messagePattern":"Task thread panicked while executing\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/bevy_tasks/src/task_pool.rs","lineNumber":624,"sourceCode":"        Self::LOCAL_EXECUTOR.with(f)\n    }\n}\n\nimpl Default for TaskPool {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl Drop for TaskPool {\n    fn drop(&mut self) {\n        self.shutdown_tx.close();\n\n        let panicking = thread::panicking();\n        for join_handle in self.threads.drain(..) {\n            let res = join_handle.join();\n            if !panicking {\n                res.expect(\"Task thread panicked while executing.\");\n            }\n        }\n    }\n}\n\n/// A [`TaskPool`] scope for running one or more non-`'static` futures.\n///\n/// For more information, see [`TaskPool::scope`].\n#[derive(Debug)]\npub struct Scope<'scope, 'env: 'scope, T> {\n    executor: &'scope crate::executor::Executor<'scope>,\n    external_executor: &'scope ThreadExecutor<'scope>,\n    scope_executor: &'scope ThreadExecutor<'scope>,\n    spawned: &'scope ConcurrentQueue<FallibleTask<Result<T, Box<dyn core::any::Any + Send>>>>,\n    // make `Scope` invariant over 'scope and 'env\n    scope: PhantomData<&'scope mut &'scope ()>,\n    env: PhantomData<&'env mut &'env ()>,\n}","sourceCodeStart":606,"sourceCodeEnd":642,"githubUrl":"https://github.com/bevyengine/bevy/blob/396ca727080776bd313bb892423b7d94e03b81b4/crates/bevy_tasks/src/task_pool.rs#L606-L642","documentation":"TaskPool::drop (task_pool.rs:620-631) closes the shutdown channel, joins every worker thread, and expects each join to succeed (skipped only when the dropping thread is already panicking). A worker's main loop wraps task execution in catch_unwind (task_pool.rs:204), so this panic means an unwind escaped that guard — typically from the on_thread_spawn/on_thread_destroy callbacks, thread-local setup/teardown, or executor machinery during shutdown.","triggerScenarios":"A panicking callback passed to TaskPoolBuilder::on_thread_spawn or on_thread_destroy; a panic in executor tick/thread-local destructors at shutdown; aborting the pool while a worker is mid-unwind.","commonSituations":"Custom thread lifecycle callbacks that panic on init failure; custom executors hooked into bevy task pools; shutdown-ordering issues in embedded Bevy hosts.","solutions":["Make on_thread_spawn/on_thread_destroy callbacks panic-free — return errors/log instead","Keep task bodies from panicking (return Result) so unwinds never reach worker teardown","Wrap risky task closures in std::panic::catch_unwind if panics are possible","Update Bevy and report if no custom callback is involved"],"exampleFix":"// before — a panic in the spawn callback kills the worker thread\nlet pool = TaskPoolBuilder::new()\n    .on_thread_spawn(|| { panic!(\"renderer init failed\"); })\n    .build();\n\n// after — log instead of panicking inside pool-owned threads\nlet pool = TaskPoolBuilder::new()\n    .on_thread_spawn(|| {\n        if let Err(e) = init_thread_local_renderer() {\n            bevy_log::error!(\"thread init failed: {e}\");\n        }\n    })\n    .build();","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// keep panics from ever escaping pool-owned threads\nlet safe_body = std::panic::AssertUnwindSafe(task_body);\nmatch std::panic::catch_unwind(safe_body) {\n    Ok(result) => result,\n    Err(payload) => {\n        bevy_log::error!(\"task panicked: {payload:?}\");\n        None // recover with a default instead of unwinding the worker\n    }\n}","preventionTips":["Make on_thread_spawn / on_thread_destroy callbacks infallible (log errors, never panic)","Prefer returning Result from tasks over asserting inside them","Install a panic hook that logs payloads so any escaping unwind is diagnosable at pool drop"],"tags":["bevy","task-pool","threads","shutdown","panic"],"backgroundTag":"worker-thread-panicked","analyzedSha":"396ca727080776bd313bb892423b7d94e03b81b4","analyzedAt":"2026-08-20T16:12:39.808Z","contentChangedAt":"2026-08-20T16:12:39.808Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}