Skip to content

Commit 287af88

Browse files
Tests & migration guide
1 parent b4c2acf commit 287af88

File tree

7 files changed

+90
-28
lines changed

7 files changed

+90
-28
lines changed

crates/bevy_app/src/app.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1350,7 +1350,8 @@ impl App {
13501350
self.default_error_handler
13511351
}
13521352

1353-
/// Override the error handler for the all subapps (including the main one).
1353+
/// Set the [default error handler] for the all subapps (including the main one and future ones)
1354+
/// that do not have one.
13541355
///
13551356
/// May only be called once and should be set by the application, not by libraries.
13561357
///
@@ -1363,12 +1364,14 @@ impl App {
13631364
/// ```
13641365
/// # use bevy_app::*;
13651366
/// # use bevy_ecs::error::warn;
1366-
/// # fn MyPlugins() {}
1367+
/// # fn MyPlugins(_: &mut App) {}
13671368
/// App::new()
13681369
/// .set_error_handler(warn)
13691370
/// .add_plugins(MyPlugins)
13701371
/// .run();
13711372
/// ```
1373+
///
1374+
/// [default error handler]: bevy_ecs::error::DefaultErrorHandler
13721375
pub fn set_error_handler(&mut self, handler: ErrorHandler) -> &mut Self {
13731376
assert!(
13741377
self.default_error_handler.is_none(),

crates/bevy_ecs/src/error/command_handling.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ where
5959
self.apply(world);
6060
}
6161
}
62+
63+
#[inline]
64+
fn handle_error(self) -> impl Command {
65+
move |world: &mut World| {
66+
self.apply(world);
67+
}
68+
}
6269
}
6370

6471
impl<C> HandleError for C

crates/bevy_ecs/src/error/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
//! [`panic`] error handler function is used, resulting in a panic with the error message attached.
99
//!
1010
//! You can change the default behavior by registering a custom error handler:
11-
//! Use [`World::set_default_error_handler`] to set a custom error handler function for a world,
11+
//! Use [`DefaultErrorHandler`] to set a custom error handler function for a world,
1212
//! or `App::set_error_handler` for a whole app.
1313
//! In practice, this is generally feature-flagged: panicking or loudly logging errors in development,
1414
//! and quietly logging or ignoring them in production to avoid crashing the app.
@@ -35,7 +35,7 @@
3535
//! context surrounding the error – such as the system's [`name`] – in your error messages.
3636
//!
3737
//! ```rust, ignore
38-
//! use bevy_ecs::error::{BevyError, ErrorContext};
38+
//! use bevy_ecs::error::{BevyError, ErrorContext, DefaultErrorHandler};
3939
//! use log::trace;
4040
//!
4141
//! fn my_error_handler(error: BevyError, ctx: ErrorContext) {
@@ -48,8 +48,8 @@
4848
//!
4949
//! fn main() {
5050
//! let mut world = World::new();
51-
//! world.set_default_error_handler(my_error_handler);
52-
//!
51+
//! world.insert_resource(DefaultErrorHandler(my_error_handler));
52+
//! // Use your world here
5353
//! }
5454
//! ```
5555
//!

crates/bevy_ecs/src/observer/runner.rs

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use core::any::Any;
33

44
use crate::{
55
component::{ComponentHook, ComponentId, HookContext, Mutable, StorageType},
6-
error::ErrorContext,
6+
error::{ErrorContext, ErrorHandler},
77
observer::{ObserverDescriptor, ObserverTrigger},
88
prelude::*,
99
query::DebugCheckedUnwrap,
@@ -190,7 +190,7 @@ pub type ObserverRunner = fn(DeferredWorld, ObserverTrigger, PtrMut, propagate:
190190
/// [`SystemParam`]: crate::system::SystemParam
191191
pub struct Observer {
192192
hook_on_add: ComponentHook,
193-
error_handler: Option<fn(BevyError, ErrorContext)>,
193+
error_handler: Option<ErrorHandler>,
194194
system: Box<dyn Any + Send + Sync + 'static>,
195195
pub(crate) descriptor: ObserverDescriptor,
196196
pub(crate) last_trigger_id: u32,
@@ -349,8 +349,6 @@ fn observer_system_runner<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
349349
return;
350350
}
351351
state.last_trigger_id = last_trigger;
352-
// SAFETY: Observer was triggered so must have an `Observer` component.
353-
let error_handler = unsafe { state.error_handler.debug_checked_unwrap() };
354352

355353
let trigger: Trigger<E, B> = Trigger::new(
356354
// SAFETY: Caller ensures `ptr` is castable to `&mut T`
@@ -378,7 +376,10 @@ fn observer_system_runner<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
378376
match (*system).validate_param_unsafe(world) {
379377
Ok(()) => {
380378
if let Err(err) = (*system).run_unsafe(trigger, world) {
381-
error_handler(
379+
let handler = state
380+
.error_handler
381+
.unwrap_or_else(|| world.default_error_handler());
382+
handler(
382383
err,
383384
ErrorContext::Observer {
384385
name: (*system).name(),
@@ -390,7 +391,10 @@ fn observer_system_runner<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
390391
}
391392
Err(e) => {
392393
if !e.skipped {
393-
error_handler(
394+
let handler = state
395+
.error_handler
396+
.unwrap_or_else(|| world.default_error_handler());
397+
handler(
394398
e.into(),
395399
ErrorContext::Observer {
396400
name: (*system).name(),
@@ -421,14 +425,10 @@ fn hook_on_add<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
421425
B::component_ids(&mut world.components_registrator(), &mut |id| {
422426
components.push(id);
423427
});
424-
let default_error_handler = world.default_error_handler();
425428
if let Some(mut observe) = world.get_mut::<Observer>(entity) {
426429
observe.descriptor.events.push(event_id);
427430
observe.descriptor.components.extend(components);
428431

429-
if observe.error_handler.is_none() {
430-
observe.error_handler = Some(default_error_handler);
431-
}
432432
let system: *mut dyn ObserverSystem<E, B> = observe.system.downcast_mut::<S>().unwrap();
433433
// SAFETY: World reference is exclusive and initialize does not touch system, so references do not alias
434434
unsafe {
@@ -441,7 +441,11 @@ fn hook_on_add<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
441441
#[cfg(test)]
442442
mod tests {
443443
use super::*;
444-
use crate::{event::Event, observer::Trigger};
444+
use crate::{
445+
error::{ignore, DefaultErrorHandler},
446+
event::Event,
447+
observer::Trigger,
448+
};
445449

446450
#[derive(Event)]
447451
struct TriggerEvent;
@@ -469,11 +473,20 @@ mod tests {
469473
Err("I failed!".into())
470474
}
471475

476+
// Using observer error handler
472477
let mut world = World::default();
473478
world.init_resource::<Ran>();
474-
let observer = Observer::new(system).with_error_handler(crate::error::ignore);
475-
world.spawn(observer);
476-
Schedule::default().run(&mut world);
479+
world.spawn(Observer::new(system).with_error_handler(ignore));
480+
world.trigger(TriggerEvent);
481+
assert!(world.resource::<Ran>().0);
482+
483+
// Using world error handler
484+
let mut world = World::default();
485+
world.init_resource::<Ran>();
486+
world.spawn(Observer::new(system));
487+
// Test that the correct handler is used when the observer was added
488+
// before the default handler
489+
world.insert_resource(DefaultErrorHandler(ignore));
477490
world.trigger(TriggerEvent);
478491
assert!(world.resource::<Ran>().0);
479492
}

crates/bevy_ecs/src/schedule/schedule.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2060,6 +2060,7 @@ mod tests {
20602060
use bevy_ecs_macros::ScheduleLabel;
20612061

20622062
use crate::{
2063+
error::{ignore, panic, DefaultErrorHandler, Result},
20632064
prelude::{ApplyDeferred, Res, Resource},
20642065
schedule::{
20652066
tests::ResMut, IntoScheduleConfigs, Schedule, ScheduleBuildSettings, SystemSet,
@@ -2809,4 +2810,32 @@ mod tests {
28092810
.expect("CheckSystemRan Resource Should Exist");
28102811
assert_eq!(value.0, 2);
28112812
}
2813+
2814+
#[test]
2815+
fn test_default_error_handler() {
2816+
#[derive(Resource, Default)]
2817+
struct Ran(bool);
2818+
2819+
fn system(mut ran: ResMut<Ran>) -> Result {
2820+
ran.0 = true;
2821+
Err("I failed!".into())
2822+
}
2823+
2824+
// Test that the default error handler is used
2825+
let mut world = World::default();
2826+
world.init_resource::<Ran>();
2827+
world.insert_resource(DefaultErrorHandler(ignore));
2828+
let mut schedule = Schedule::default();
2829+
schedule.add_systems(system).run(&mut world);
2830+
assert!(world.resource::<Ran>().0);
2831+
2832+
// Test that the handler doesn't change within the schedule
2833+
schedule.add_systems(
2834+
(|world: &mut World| {
2835+
world.insert_resource(DefaultErrorHandler(panic));
2836+
})
2837+
.before(system),
2838+
);
2839+
schedule.run(&mut world);
2840+
}
28122841
}

crates/bevy_ecs/src/system/commands/mod.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ use crate::{
8888
/// A [`Command`] can return a [`Result`](crate::error::Result),
8989
/// which will be passed to an [error handler](crate::error) if the `Result` is an error.
9090
///
91-
/// The [default error handler](crate::error::default_error_handler) panics.
92-
/// It can be configured via [`set_global_default_error_handler`](crate::error::set_global_default_error_handler).
91+
/// The default error handler panics. It can be configured via
92+
/// the [`DefaultErrorHandler`](crate::error::DefaultErrorHandler) resource.
9393
///
9494
/// Alternatively, you can customize the error handler for a specific command
9595
/// by calling [`Commands::queue_handled`].
@@ -508,7 +508,7 @@ impl<'w, 's> Commands<'w, 's> {
508508
/// Pushes a generic [`Command`] to the command queue.
509509
///
510510
/// If the [`Command`] returns a [`Result`],
511-
/// it will be handled using the [default error handler](crate::error::default_error_handler).
511+
/// it will be handled using the [default error handler](crate::error::DefaultErrorHandler).
512512
///
513513
/// To use a custom error handler, see [`Commands::queue_handled`].
514514
///
@@ -643,7 +643,7 @@ impl<'w, 's> Commands<'w, 's> {
643643
/// This command will fail if any of the given entities do not exist.
644644
///
645645
/// It will internally return a [`TryInsertBatchError`](crate::world::error::TryInsertBatchError),
646-
/// which will be handled by the [default error handler](crate::error::default_error_handler).
646+
/// which will be handled by the [default error handler](crate::error::DefaultErrorHandler).
647647
#[track_caller]
648648
pub fn insert_batch<I, B>(&mut self, batch: I)
649649
where
@@ -674,7 +674,7 @@ impl<'w, 's> Commands<'w, 's> {
674674
/// This command will fail if any of the given entities do not exist.
675675
///
676676
/// It will internally return a [`TryInsertBatchError`](crate::world::error::TryInsertBatchError),
677-
/// which will be handled by the [default error handler](crate::error::default_error_handler).
677+
/// which will be handled by the [default error handler](crate::error::DefaultErrorHandler).
678678
#[track_caller]
679679
pub fn insert_batch_if_new<I, B>(&mut self, batch: I)
680680
where
@@ -1175,8 +1175,8 @@ impl<'w, 's> Commands<'w, 's> {
11751175
/// An [`EntityCommand`] can return a [`Result`](crate::error::Result),
11761176
/// which will be passed to an [error handler](crate::error) if the `Result` is an error.
11771177
///
1178-
/// The [default error handler](crate::error::default_error_handler) panics.
1179-
/// It can be configured via [`set_global_default_error_handler`](crate::error::set_global_default_error_handler).
1178+
/// The default error handler panics. It can be configured via
1179+
/// the [`DefaultErrorHandler`](crate::error::DefaultErrorHandler) resource.
11801180
///
11811181
/// Alternatively, you can customize the error handler for a specific command
11821182
/// by calling [`EntityCommands::queue_handled`].
@@ -1768,7 +1768,7 @@ impl<'a> EntityCommands<'a> {
17681768
/// Pushes an [`EntityCommand`] to the queue,
17691769
/// which will get executed for the current [`Entity`].
17701770
///
1771-
/// The [default error handler](crate::error::default_error_handler)
1771+
/// The [default error handler](crate::error::DefaultErrorHandler)
17721772
/// will be used to handle error cases.
17731773
/// Every [`EntityCommand`] checks whether the entity exists at the time of execution
17741774
/// and returns an error if it does not.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
title: Global default error handler
3+
pull_requests: [18810]
4+
---
5+
6+
Worlds can now have different default error handlers, so there no longer is a global handler.
7+
8+
Replace uses of `GLOBAL_ERROR_HANDLER` with `App`'s `.set_error_handler(handler)`.
9+
For worlds that do not directly belong to an `App`/`SubApp`,
10+
insert the `DefaultErrorHandler(handler)` resource.

0 commit comments

Comments
 (0)