Skip to content

Commit 8f75a4d

Browse files
committed
add a more helpful error to help debug panicking command on despawned entity (#5198)
# Objective - Help users fix issue when their app panic when executing a command on a despawned entity ## Solution - Add an error code and a page describing how to debug the issue
1 parent 40982cd commit 8f75a4d

File tree

3 files changed

+97
-9
lines changed

3 files changed

+97
-9
lines changed

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

+3-9
Original file line numberDiff line numberDiff line change
@@ -712,9 +712,7 @@ pub struct Despawn {
712712
impl Command for Despawn {
713713
fn write(self, world: &mut World) {
714714
if !world.despawn(self.entity) {
715-
warn!("Could not despawn entity {:?} because it doesn't exist in this World.\n\
716-
If this command was added to a newly spawned entity, ensure that you have not despawned that entity within the same stage.\n\
717-
This may have occurred due to system order ambiguity, or if the spawning system has multiple command buffers", self.entity);
715+
warn!("error[B0003]: Could not despawn entity {:?} because it doesn't exist in this World.", self.entity);
718716
}
719717
}
720718
}
@@ -732,9 +730,7 @@ where
732730
if let Some(mut entity) = world.get_entity_mut(self.entity) {
733731
entity.insert_bundle(self.bundle);
734732
} else {
735-
panic!("Could not insert a bundle (of type `{}`) for entity {:?} because it doesn't exist in this World.\n\
736-
If this command was added to a newly spawned entity, ensure that you have not despawned that entity within the same stage.\n\
737-
This may have occurred due to system order ambiguity, or if the spawning system has multiple command buffers", std::any::type_name::<T>(), self.entity);
733+
panic!("error[B0003]: Could not insert a bundle (of type `{}`) for entity {:?} because it doesn't exist in this World.", std::any::type_name::<T>(), self.entity);
738734
}
739735
}
740736
}
@@ -753,9 +749,7 @@ where
753749
if let Some(mut entity) = world.get_entity_mut(self.entity) {
754750
entity.insert(self.component);
755751
} else {
756-
panic!("Could not add a component (of type `{}`) to entity {:?} because it doesn't exist in this World.\n\
757-
If this command was added to a newly spawned entity, ensure that you have not despawned that entity within the same stage.\n\
758-
This may have occurred due to system order ambiguity, or if the spawning system has multiple command buffers", std::any::type_name::<T>(), self.entity);
752+
panic!("error[B0003]: Could not add a component (of type `{}`) to entity {:?} because it doesn't exist in this World.", std::any::type_name::<T>(), self.entity);
759753
}
760754
}
761755
}

errors/B0003.md

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# B0003
2+
3+
As commands are executed asynchronously, it is possible to issue a command on an entity that will no longer exist at the time of the command execution.
4+
5+
Erroneous code example:
6+
7+
```rust,should_panic
8+
use bevy::prelude::*;
9+
10+
fn main() {
11+
App::new()
12+
.add_plugins(DefaultPlugins)
13+
.add_startup_system(setup)
14+
.add_system(despawning)
15+
.add_system(use_entity.after(despawning))
16+
.run();
17+
}
18+
19+
struct MyEntity(Entity);
20+
21+
#[derive(Component)]
22+
struct Hello;
23+
24+
fn setup(mut commands: Commands) {
25+
let entity = commands.spawn().id();
26+
commands.insert_resource(MyEntity(entity));
27+
}
28+
29+
fn despawning(mut commands: Commands, entity: Option<Res<MyEntity>>) {
30+
if let Some(my_entity) = entity {
31+
commands.entity(my_entity.0).despawn();
32+
commands.remove_resource::<MyEntity>();
33+
}
34+
}
35+
36+
fn use_entity(mut commands: Commands, entity: Option<Res<MyEntity>>) {
37+
if let Some(my_entity) = entity {
38+
commands.entity(my_entity.0).insert(Hello);
39+
}
40+
}
41+
```
42+
43+
This will panic, as system `use_entity` is executed after system `despawning`. Without the system ordering specified here, the ordering would be random and this code would panic half the time.
44+
45+
The default panic message is telling you the entity id (`0v0`) and the command that failed (adding a component `Hello`):
46+
47+
```text
48+
thread 'main' panicked at 'error[B0003]: Could not add a component (of type `use_entity_after_despawn::Hello`) to entity 0v0 because it doesn't exist in this World.', /bevy/crates/bevy_ecs/src/system/commands/mod.rs:752:13
49+
```
50+
51+
But you don't know which system tried to add a component, and which system despawned the entity.
52+
53+
To get the system that created the command that panics, you can enable the `trace` feature of Bevy. This will add a panic handler that will print more informations:
54+
55+
```text
56+
0: bevy_ecs::schedule::stage::system_commands
57+
with name="use_entity_after_despawn::use_entity"
58+
at crates/bevy_ecs/src/schedule/stage.rs:880
59+
1: bevy_ecs::schedule::stage
60+
with name=Update
61+
at crates/bevy_ecs/src/schedule/mod.rs:337
62+
2: bevy_app::app::frame
63+
at crates/bevy_app/src/app.rs:113
64+
3: bevy_app::app::bevy_app
65+
at crates/bevy_app/src/app.rs:126
66+
thread 'main' panicked at 'error[B0003]: Could not add a component (of type `use_entity_after_despawn::Hello`) to entity 0v0 because it doesn't exist in this World.', /bevy/crates/bevy_ecs/src/system/commands/mod.rs:752:13
67+
```
68+
69+
From the first two lines, you now know that it panics while executing a command from the system `use_entity`.
70+
71+
To get the system that created the despawn command, you can enable DEBUG logs for crate `bevy_ecs`, for example by setting the environment variable `RUST_LOG=bevy_ecs=debug`. This will log:
72+
73+
```text
74+
DEBUG stage{name=Update}:system_commands{name="use_entity_after_despawn::despawning"}: bevy_ecs::world: Despawning entity 0v0
75+
thread 'main' panicked at 'error[B0003]: Could not add a component (of type `use_entity_after_despawn::Hello`) to entity 0v0 because it doesn't exist in this World.', /bevy/crates/bevy_ecs/src/system/commands/mod.rs:752:13
76+
```
77+
78+
From the first line, you know the entity `0v0` was despawned when executing a command from system `despawning`. In a real case, you could have many log lines, you will need to search for the exact entity from the panic message.
79+
80+
Combining those two, you should get enough informations to understand why this panic is happening and how to fix it:
81+
82+
```text
83+
DEBUG stage{name=Update}:system_commands{name="use_entity_after_despawn::despawning"}: bevy_ecs::world: Despawning entity 0v0
84+
0: bevy_ecs::schedule::stage::system_commands
85+
with name="use_entity_after_despawn::use_entity"
86+
at crates/bevy_ecs/src/schedule/stage.rs:880
87+
1: bevy_ecs::schedule::stage
88+
with name=Update
89+
at crates/bevy_ecs/src/schedule/mod.rs:337
90+
thread 'main' panicked at 'error[B0003]: Could not add a component (of type `use_entity_after_despawn::Hello`) to entity 0v0 because it doesn't exist in this World.', /bevy/crates/bevy_ecs/src/system/commands/mod.rs:752:13
91+
```

errors/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ pub struct B0001;
33

44
#[doc = include_str!("../B0002.md")]
55
pub struct B0002;
6+
7+
#[doc = include_str!("../B0003.md")]
8+
pub struct B0003;

0 commit comments

Comments
 (0)