bevyengine / bevyengine/bevy

Archetype bug panics when calling EntityWorldMut::observe several times before calling EntityWorldMut::insert

Open
#18,844 0 comments 0 reactions 0 assignees View on GitHub
A-ECS C-Bug I-Crash S-Needs-Investigation
Dominant language
Rust
Stars
48.2k
Forks
4.8k
Avg merge
3d 16h
Merged PRs (30d)
171

Description

## Bevy version

`v0.15.3`

## Context
I have a command that spawns an entity in a fairly complex process, making use of exclusive world access and `EntityWorldMut` to optionally insert components covering a moderate spread of functionality, and I added a secondary `EntityWorldMut::observe` call in the midst of the function only to find that the game would throw a weird subtract underflow error @ `bevy_ecs-0.15.3/src/archetype.rs:587:38:`. Through tracing, I found out that the program hadn't panic'd when calling `observe`, but instead later on in my function when calling `EntityWorldMut::insert`. So I wrote out what I thought was the issue in the following.

### Source code for reproducing issue

```rust
use bevy::prelude::*;

#[test]
fn observe_underflow() {
println!("Started test");

let mut world = World::new();

let mut entity: EntityWorldMut = world.spawn_empty();

println!("Spawned empty entity");

entity.observe(|_trigger: Trigger| ());
entity.observe(|_trigger: Trigger| ());

println!("Observed both triggers");

entity.insert(Bar);

println!("Didn't panic")
}

#[derive(Event)]
struct Foo;

#[derive(Event)]
struct Foob;

#[derive(Component)]
struct Bar;
```

## What went wrong

When running the testing code snippet provided above, the test panics when calling `entity.insert(Bar);` thanks to the two lines calling `entity.observe(|_trigger: Trigger| {});. (See the screenshot below)

## Additional information

To fix the test, any of the calls on `entity` can be commented out to get the test to run successfully.
Alternatively, you can move `entity.insert(Bar);` to earlier in the test which also avoids a panic.

To apply the workaround broadly, make sure that any `.observe` calls are made only after the entity has had all of it's components completely inserted.

Run of the failing test
![Image](https://github.com/user-attachments/assets/4155fc95-37f9-42f0-95d8-047855796e68)


stdout

```
Finished `test` profile [optimized + debuginfo] target(s) in 2.21s
Running unittests src/lib.rs (target/debug/deps/short_flight-cc6f80c42f537f0d)

running 1 test
test test::observe_underflow ... FAILED

failures:

---- test::observe_underflow stdout ----
Started test
Spawned empty entity
Observed both triggers

thread 'test::observe_underflow' panicked at /home/codespace/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bevy_ecs-0.15.3/src/archetype.rs:587:38:
attempt to subtract with overflow
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

failures:
test::observe_underflow

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass `--lib`
```

Contributor guide

Open the contributing guide

Research direction

Start by reproducing the test with EntityWorldMut::observe called twice before EntityWorldMut::insert, then trace the panic reported at bevy_ecs-0.15.3/src/archetype.rs:587:38. Inspect the EntityWorldMut observe and insert paths and add a regression test for this sequence. Done means the reproduction no longer panics and the test passes.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
game-dev
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.