Skip to content

Conversation

shanecelis
Copy link
Contributor

@shanecelis shanecelis commented Oct 21, 2025

Objective

Fix the stackoverflow on asset reload when asset contains its own path as a dependency.

Problem

There is a way to create a circular dependency graph with an asset loader. I don't know how I managed to do it. I have tried to create a minimal example, but it has not exhibited the error yet. But I do have a means of exhibiting the error with my project Nano-9, reproduction details below.

Solution

This commit has two fixes: one at the insertion point (introducing self-reference), and one at the recursion point (following self-reference).

Insertion point

Issue warning when an asset wants to mark itself as a dependency and do not allow inserting itself as a dependent.

Recursion point

Check for self loops. Warn on self detection and do not loop.

It's likely that if you fix it at the insertion point, you don't need to worry about it at the recursion point. You will have stopped the cause of the issue. I left both in for transparency about where the issue lies so far as I could see.

Testing

I can reproduce this error with an example from my Nano-9 project. I wish it were a minimal example. It's not, but I have put it on a branch to isolate this issue. It uses my Bevy fork that is v0.16.1 plus a commit tagged v0.16.1b, which was required for it to build. This PR is a cherry pick of the fix commit against Bevy's main branch.

I can reproduce this error by doing the following:

git clone -b bevy-asset-stackoverflow https://github.com/shanecelis/nano-9.git
cd nano-9
cargo run --example sprite --features watcher --no-default-features &
touch assets/BirdSprite.png

Here is an excerpt of the crash report on macOS 15.6.1, M4 Max:

...
Thread 0 Crashed:: main Dispatch queue: com.apple.main-thread
0   libsystem_kernel.dylib        	       0x18cc2a388 __pthread_kill + 8
1   libsystem_pthread.dylib       	       0x18cc6388c pthread_kill + 296
2   libsystem_c.dylib             	       0x18cb6ca3c abort + 124
3   sprite                        	       0x10598d09c std::sys::pal::unix::abort_internal::h1edcc850f5dec78e + 12
4   sprite                        	       0x10598c5b0 std::process::abort::hffd6db68ff0662a6 + 12
5   sprite                        	       0x10580d464 std::sys::pal::unix::stack_overflow::imp::signal_handler::h7b8eae417c5ee98d + 604
6   libsystem_platform.dylib      	       0x18cc9d6a4 _sigtramp + 56
7   sprite                        	       0x10573f890 _$LT$std..path..Path$u20$as$u20$core..hash..Hash$GT$::hash::h732e05949b6a170e + 136
8   sprite                        	       0x10573f890 _$LT$std..path..Path$u20$as$u20$core..hash..Hash$GT$::hash::h732e05949b6a170e + 136
9   sprite                        	       0x104d5f278 _$LT$atomicow..CowArc$LT$T$GT$$u20$as$u20$core..hash..Hash$GT$::hash::h6c45383281764a05 + 40
10  sprite                        	       0x104ed62d8 _$LT$bevy_asset..path..AssetPath$u20$as$u20$core..hash..Hash$GT$::hash::h11b348528182d76d + 52
11  sprite                        	       0x104ded598 hashbrown::map::make_hash::hb7812997186aa817 + 56
12  sprite                        	       0x104de8eec hashbrown::map::HashMap$LT$K$C$V$C$S$C$A$GT$::insert::h3a8b107dcf9615e9 + 64
13  sprite                        	       0x104da7264 hashbrown::set::HashSet$LT$T$C$S$C$A$GT$::insert::h44e988e7a7752688 + 24
14  sprite                        	       0x104e7b15c bevy_platform::collections::hash_set::HashSet$LT$T$C$S$GT$::insert::hb1d23d506d548fcc + 24
15  sprite                        	       0x104dba770 bevy_asset::server::handle_internal_asset_events::_$u7b$$u7b$closure$u7d$$u7d$::queue_ancestors::hd1670687bb18ae9f + 216
16  sprite                        	       0x104dba780 bevy_asset::server::handle_internal_asset_events::_$u7b$$u7b$closure$u7d$$u7d$::queue_ancestors::hd1670687bb18ae9f + 232
17  sprite                        	       0x104dba780 bevy_asset::server::handle_internal_asset_events::_$u7b$$u7b$closure$u7d$$u7d$::queue_ancestors::hd1670687bb18ae9f + 232
18  sprite                        	       0x104dba780 bevy_asset::server::handle_internal_asset_events::_$u7b$$u7b$closure$u7d$$u7d$::queue_ancestors::hd1670687bb18ae9f + 232
19  sprite                        	       0x104dba780 bevy_asset::server::handle_internal_asset_events::_$u7b$$u7b$closure$u7d$$u7d$::queue_ancestors::hd1670687bb18ae9f + 232
...

Exercising the fix

Alter Nano-9's Cargo file to use the fix branch:

-bevy = { git = "https://github.com/shanecelis/bevy.git", tag = "v0.16.1b" }
+bevy = { git = "https://github.com/shanecelis/bevy.git", branch = "fix/asset-reload-overflow" }

Run the same command again, and you will see a warning:

2025-10-21T04:10:52.348726Z  WARN bevy_asset::server::info: Asset 'BirdSprite.png' wants to treat itself as a dependency

Alternative Solution

This commit fixes the immediate stackoverflow issue; however, it would be even better if the user were prevented from making a circular dependency graph in the first place.

There is a way to create a circular dependency graph with asset loaders.
I don't know how I managed to do it. I have tried to create a minimal
example, but it has not exhibited the error yet. This commit has two
fixes: one at the recursion point, and one at the insertion point.

Check for self loops. Warn on self detection and do not loop.

Issue warning when an asset wants to mark itself as a dependency and do
not allow inserting itself as a dependent.
@alice-i-cecile alice-i-cecile added C-Bug An unexpected or incorrect behavior A-Assets Load files from disk to use for things like images, models, and sounds S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged labels Oct 21, 2025
@alice-i-cecile
Copy link
Member

This seems like a reasonable fix but I'd really like to add a test along with this PR.

Copy link
Contributor

@andriyDev andriyDev left a comment

Choose a reason for hiding this comment

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

This also seems sensible to me, but I would also like to block on a test for this.

@shanecelis
Copy link
Contributor Author

Understood. Are there any asset-loader tests that I might emulate?

@andriyDev
Copy link
Contributor

Pretty much all the tests in crates/bevy_asset/src/lib.rs are sensible tests to emulate. Here's a test for nested immediate loading

fn error_on_nested_immediate_load_of_subasset() {
which could help.

@shanecelis
Copy link
Contributor Author

Thank you for the test reference. They've been very helpful.

Coming up with a test has been tricky but instructive. I finally figured out how to provoke the error. If I use an immediate load in an asset loader that tries to load its own asset path, it will cause a stack overflow, which is the behavior I originally saw.

A asset loader with a self-path deferred load's does not provoke an error or overflow, but it does behave differently than a normal asset. It will emit asset Added and Modified but there won't be any LoadedWithDependencies event.

I'll try to get these tests in with some better fixes tomorrow night.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Assets Load files from disk to use for things like images, models, and sounds C-Bug An unexpected or incorrect behavior S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants