Skip to content

Pass panics to the fallback error handler#24240

Open
SpecificProtagonist wants to merge 13 commits into
bevyengine:mainfrom
SpecificProtagonist:panic-to-error
Open

Pass panics to the fallback error handler#24240
SpecificProtagonist wants to merge 13 commits into
bevyengine:mainfrom
SpecificProtagonist:panic-to-error

Conversation

@SpecificProtagonist
Copy link
Copy Markdown
Contributor

@SpecificProtagonist SpecificProtagonist commented May 11, 2026

Objective

Currently, a panic (whether from engine or user code, as there is little distinction) takes down the entire app with it. Instead the user should be able to decide how the error is handled. This is currently not possible except by writing your own executor and setting it for all relevant schedules.

See for comparison Godot's policy on exceptions:

Why does Godot not use exceptions?

We believe games should not crash, no matter what. If an unexpected situation happens, Godot > will print an error (which can be traced even to script), but then it will try to recover as > gracefully as possible and keep going.

Unity will also log an error and then continue if user code throws an exception. I believe Unreal does too for exceptions coming from Blueprints. Similarly, many web servers will respond with an error to a request that threw an exception, but will not crash the server itself.

This PR does not enable this behavior by default, but makes it user-configurable.

Also fixes #19109
Also (I think) fixes #7434

Solution

Instead of rethrowing panics, hand them to the FallbackErrorHandler.

If the panic was thrown by an error handler in the first place, we don't need to pass it back to a handler again. I've added a way for the error handler to signal that it's the source of the panic.

The constructed error is created without a backtrace, as the default panic handler already prints it when instructed to via RUST_LIB_BACKTRACE/RUST_BACKTRACE.

Panics will not be turned into errors on no_std projects.

Testing

See added panic_to_error test

@SpecificProtagonist SpecificProtagonist added C-Feature A new feature, making something new possible A-ECS Entities, components, systems, and events S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels May 11, 2026
@github-project-automation github-project-automation Bot moved this to Needs SME Triage in ECS May 11, 2026
Err(payload) if PANIC_ORIGINATES_FROM_ERROR_HANDLER.replace(false) => {
(None, Err(payload))
}
// We can ignore the panic payload here, as it will have already been printed.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By default, the panic handler will have already printed the panic payload, but we could add it to the error message given to the error handler too.

Err(_) => {
let err = BevyError::new_with_backtrace(
Severity::Panic,
"System panicked",
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm using only an error message here, but if desired I could create a structured error that can be BevyError::downcast_refed.

Comment on lines +134 to +137
/// When deliberately throwing a panic in your [`ErrorHandler`],
/// set this to true to indicate to the executor that the panic
/// should not be turned back into a [`BevyError`].
pub static PANIC_ORIGINATES_FROM_ERROR_HANDLER: core::cell::Cell<bool> = const {core::cell::Cell::new(false)};
Copy link
Copy Markdown
Contributor Author

@SpecificProtagonist SpecificProtagonist May 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not particularly elegant, but works and is easier to use than if for all error handlers we were to force a wrapper that set this to true, calls the error handler and then sets it to false again.

Copy link
Copy Markdown
Contributor

@chescock chescock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, being able to recover from panics seems like a really important part of making Bevy robust!

Comment thread crates/bevy_ecs/src/schedule/executor/multi_threaded.rs Outdated
Comment thread crates/bevy_ecs/src/schedule/executor/multi_threaded.rs Outdated
Comment thread crates/bevy_ecs/src/schedule/executor/multi_threaded.rs Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-ECS Entities, components, systems, and events C-Feature A new feature, making something new possible S-Needs-Review Needs reviewer attention (from anyone!) to move forward

Projects

Status: Needs SME Triage

Development

Successfully merging this pull request may close these issues.

Panics cannot be caught on iOS Add panicking system test to bevy_ecs

2 participants