From 29f779bed332d68b20ffc6287ae3fc2d815c7bf8 Mon Sep 17 00:00:00 2001 From: Greeble <166992735+greeble-dev@users.noreply.github.com> Date: Tue, 20 Jan 2026 08:52:50 +0000 Subject: [PATCH 1/9] First pass. --- crates/bevy_asset/src/lib.rs | 105 +++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index 949a0c7529ac4..2375d28ed8377 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -2906,4 +2906,109 @@ mod tests { // Now that the dependency is loaded, the subasset is counted as loaded with dependencies! assert!(asset_server.is_loaded_with_dependencies(&subasset_handle)); } + + // `LoadState` without the failure message, which makes it easier to compare. + #[derive(Debug, PartialEq, Eq)] + enum SimpleLoadState { + NotLoaded, + Loading, + Loaded, + Failed, + } + + impl From for SimpleLoadState { + fn from(value: LoadState) -> Self { + match value { + LoadState::NotLoaded => Self::NotLoaded, + LoadState::Loading => Self::Loading, + LoadState::Loaded => Self::Loaded, + LoadState::Failed(_) => Self::Failed, + } + } + } + + #[test] + fn load_fail() { + let (mut app, dir) = create_app(); + + app.init_asset::() + .init_asset::() + .register_asset_loader(CoolTextLoader); + + let a_path = "a.cool.ron"; + let a_ron = r#" +( + text: "a", + dependencies: [], + embedded_dependencies: [], + sub_texts: ["subasset"], +)"#; + + dir.insert_asset_text(Path::new(a_path), a_ron); + + let asset_server = app.world().resource::().clone(); + + let tests: &[(UntypedHandle, SimpleLoadState, &'static str)] = &[ + ( + asset_server.load::("a.cool.ron").untyped(), + SimpleLoadState::Loaded, + "root asset exists", + ), + ( + asset_server.load::("a.cool.ron").untyped(), + SimpleLoadState::Failed, + "root asset is wrong type", + ), + ( + asset_server + .load::("does_not_exist.cool.ron") + .untyped(), + SimpleLoadState::Failed, + "root asset does not exist", + ), + ( + asset_server + .load::("a.cool.ron#subasset") + .untyped(), + SimpleLoadState::Loaded, + "sub-asset exists", + ), + ( + asset_server + .load::("a.cool.ron#subasset") + .untyped(), + //SimplifiedLoadState::Failed, + SimpleLoadState::Loading, + "sub-asset is wrong type", + ), + ( + asset_server + .load::("a.cool.ron#does_not_exist") + .untyped(), + //SimplifiedLoadState::Failed, + SimpleLoadState::Loading, + "sub-asset does not exist", + ), + ]; + + for (handle, expected_load_state, label) in tests { + let mut load_state = SimpleLoadState::NotLoaded; + + for _ in 0..LARGE_ITERATION_COUNT { + app.update(); + load_state = asset_server.get_load_state(handle).unwrap().into(); + if load_state == *expected_load_state { + break; + } + } + + assert!( + load_state == *expected_load_state, + "For test \"{}\", expected {:?} but got {:?}.", + label, + expected_load_state, + load_state, + ); + } + } } From 5241f1a07603a9ef86ab925137972343e1a66639 Mon Sep 17 00:00:00 2001 From: Greeble <166992735+greeble-dev@users.noreply.github.com> Date: Tue, 20 Jan 2026 14:32:57 +0000 Subject: [PATCH 2/9] Redid test as a simpler function. Fixed misleading "root asset is wrong type" test. --- crates/bevy_asset/src/lib.rs | 166 ++++++++++++++++++++--------------- 1 file changed, 93 insertions(+), 73 deletions(-) diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index 2375d28ed8377..4bb294c52d22b 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -2907,108 +2907,128 @@ mod tests { assert!(asset_server.is_loaded_with_dependencies(&subasset_handle)); } - // `LoadState` without the failure message, which makes it easier to compare. + // A simplified version of `LoadState` for easier comparison. #[derive(Debug, PartialEq, Eq)] - enum SimpleLoadState { + enum TestLoadState { NotLoaded, Loading, Loaded, - Failed, + Failed(TestAssetLoadError), } - impl From for SimpleLoadState { - fn from(value: LoadState) -> Self { + // A simplified subset of `AssetLoadError` for easier comparison. + #[derive(Debug, PartialEq, Eq)] + enum TestAssetLoadError { + MissingAssetLoader, + AssetReaderErrorNotFound, + } + + impl From<&LoadState> for TestLoadState { + fn from(value: &LoadState) -> Self { match value { LoadState::NotLoaded => Self::NotLoaded, LoadState::Loading => Self::Loading, LoadState::Loaded => Self::Loaded, - LoadState::Failed(_) => Self::Failed, + LoadState::Failed(err) => Self::Failed((&**err).into()), } } } - #[test] - fn load_fail() { + impl From<&AssetLoadError> for TestAssetLoadError { + fn from(value: &AssetLoadError) -> TestAssetLoadError { + match value { + AssetLoadError::MissingAssetLoader { .. } => Self::MissingAssetLoader, + AssetLoadError::AssetReaderError(AssetReaderError::NotFound(_)) => { + Self::AssetReaderErrorNotFound + } + _ => todo!("{:?}", value), + } + } + } + + // An asset type that's registered but doesn't have a loader. + #[derive(Asset, TypePath)] + struct LoaderlessAsset; + + // Load the given path and check that the eventual load state matches. + fn test_load_state( + label: &'static str, + path: &'static str, + expect_load_state: TestLoadState, + ) { let (mut app, dir) = create_app(); app.init_asset::() .init_asset::() + .init_asset::() .register_asset_loader(CoolTextLoader); - let a_path = "a.cool.ron"; - let a_ron = r#" + dir.insert_asset_text( + Path::new("test.cool.ron"), + r#" ( - text: "a", + text: "test", dependencies: [], embedded_dependencies: [], sub_texts: ["subasset"], -)"#; - - dir.insert_asset_text(Path::new(a_path), a_ron); +)"#, + ); let asset_server = app.world().resource::().clone(); + let handle = asset_server.load::(path); + let mut load_state = LoadState::NotLoaded; - let tests: &[(UntypedHandle, SimpleLoadState, &'static str)] = &[ - ( - asset_server.load::("a.cool.ron").untyped(), - SimpleLoadState::Loaded, - "root asset exists", - ), - ( - asset_server.load::("a.cool.ron").untyped(), - SimpleLoadState::Failed, - "root asset is wrong type", - ), - ( - asset_server - .load::("does_not_exist.cool.ron") - .untyped(), - SimpleLoadState::Failed, - "root asset does not exist", - ), - ( - asset_server - .load::("a.cool.ron#subasset") - .untyped(), - SimpleLoadState::Loaded, - "sub-asset exists", - ), - ( - asset_server - .load::("a.cool.ron#subasset") - .untyped(), - //SimplifiedLoadState::Failed, - SimpleLoadState::Loading, - "sub-asset is wrong type", - ), - ( - asset_server - .load::("a.cool.ron#does_not_exist") - .untyped(), - //SimplifiedLoadState::Failed, - SimpleLoadState::Loading, - "sub-asset does not exist", - ), - ]; + // XXX TODO: Is there a way to know that the load state is final? + for _ in 0..LARGE_ITERATION_COUNT { + app.update(); + load_state = asset_server.get_load_state(&handle).unwrap(); + if TestLoadState::from(&load_state) == expect_load_state { + break; + } + } - for (handle, expected_load_state, label) in tests { - let mut load_state = SimpleLoadState::NotLoaded; + assert!( + TestLoadState::from(&load_state) == expect_load_state, + "For test \"{}\", expected {:?} but got {:?}.", + label, + expect_load_state, + load_state, + ); + } - for _ in 0..LARGE_ITERATION_COUNT { - app.update(); - load_state = asset_server.get_load_state(handle).unwrap().into(); - if load_state == *expected_load_state { - break; - } - } + #[test] + fn load_fail() { + test_load_state::("root asset exists", "test.cool.ron", TestLoadState::Loaded); - assert!( - load_state == *expected_load_state, - "For test \"{}\", expected {:?} but got {:?}.", - label, - expected_load_state, - load_state, - ); - } + test_load_state::( + "root asset does not exist", + "does_not_exist.cool.ron", + TestLoadState::Failed(TestAssetLoadError::AssetReaderErrorNotFound), + ); + test_load_state::( + "sub-asset exists", + "test.cool.ron#subasset", + TestLoadState::Loaded, + ); + + test_load_state::( + "sub-asset does not exist", + "test.cool.ron#does_not_exist", + // XXX TODO: This is broken. + TestLoadState::Loading, + ); + + test_load_state::( + "sub-asset exists but is wrong type", + "test.cool.ron#subasset", + // XXX TODO: This is broken. + TestLoadState::Loading, + ); + + test_load_state::( + "root asset has no loader", + "loaderless", + TestLoadState::Failed(TestAssetLoadError::MissingAssetLoader), + ); } } From a22ca5460cab23e6696ae8781d3da5e484b15fc6 Mon Sep 17 00:00:00 2001 From: Greeble <166992735+greeble-dev@users.noreply.github.com> Date: Tue, 20 Jan 2026 14:49:11 +0000 Subject: [PATCH 3/9] Fixed missing sub-asset errors failing to set `LoadState::Failed`, at least for the case where the sub-asset type is known. --- crates/bevy_asset/src/lib.rs | 5 +++-- crates/bevy_asset/src/server/mod.rs | 12 ++++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index 4bb294c52d22b..504e7b83f6332 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -2921,6 +2921,7 @@ mod tests { enum TestAssetLoadError { MissingAssetLoader, AssetReaderErrorNotFound, + MissingLabel, } impl From<&LoadState> for TestLoadState { @@ -2941,6 +2942,7 @@ mod tests { AssetLoadError::AssetReaderError(AssetReaderError::NotFound(_)) => { Self::AssetReaderErrorNotFound } + AssetLoadError::MissingLabel { .. } => Self::MissingLabel, _ => todo!("{:?}", value), } } @@ -3014,8 +3016,7 @@ mod tests { test_load_state::( "sub-asset does not exist", "test.cool.ron#does_not_exist", - // XXX TODO: This is broken. - TestLoadState::Loading, + TestLoadState::Failed(TestAssetLoadError::MissingLabel), ); test_load_state::( diff --git a/crates/bevy_asset/src/server/mod.rs b/crates/bevy_asset/src/server/mod.rs index e9eb53f532934..770b83da4cb2e 100644 --- a/crates/bevy_asset/src/server/mod.rs +++ b/crates/bevy_asset/src/server/mod.rs @@ -847,11 +847,19 @@ impl AssetServer { .map(|s| (**s).to_owned()) .collect(); all_labels.sort_unstable(); - return Err(AssetLoadError::MissingLabel { + let error = AssetLoadError::MissingLabel { base_path, label: label.to_string(), all_labels, - }); + }; + if let Some(asset_id) = asset_id { + self.send_asset_event(InternalAssetEvent::Failed { + index: asset_id, + error: error.clone(), + path: path.into_owned(), + }); + } + return Err(error); } } } else { From 1295646058b6e36e5969d17109cb3f837d82296b Mon Sep 17 00:00:00 2001 From: Greeble <166992735+greeble-dev@users.noreply.github.com> Date: Tue, 20 Jan 2026 15:43:51 +0000 Subject: [PATCH 4/9] Fixed sub-assets getting stuck in the loading state when the sub-asset is not of the requested type. --- crates/bevy_asset/src/server/mod.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/crates/bevy_asset/src/server/mod.rs b/crates/bevy_asset/src/server/mod.rs index 770b83da4cb2e..cbd889b249456 100644 --- a/crates/bevy_asset/src/server/mod.rs +++ b/crates/bevy_asset/src/server/mod.rs @@ -839,7 +839,25 @@ impl AssetServer { Ok(loaded_asset) => { let final_handle = if let Some(label) = path.label_cow() { match loaded_asset.labeled_assets.get(&label) { - Some(labeled_asset) => Some(labeled_asset.handle.clone()), + Some(labeled_asset) => { + if let Some(asset_id) = asset_id + && asset_id.type_id != labeled_asset.handle.type_id() + { + let error = AssetLoadError::RequestedHandleTypeMismatch { + path: path.clone(), + requested: asset_id.type_id, + actual_asset_name: labeled_asset.asset.value.asset_type_name(), + loader_name: loader.type_path(), + }; + self.send_asset_event(InternalAssetEvent::Failed { + index: asset_id, + error: error.clone(), + path: path.into_owned(), + }); + return Err(error); + } + Some(labeled_asset.handle.clone()) + } None => { let mut all_labels: Vec = loaded_asset .labeled_assets From f8d3ceba034ded1e3ffa25427a76f1a6db3b9aa7 Mon Sep 17 00:00:00 2001 From: Greeble <166992735+greeble-dev@users.noreply.github.com> Date: Tue, 20 Jan 2026 15:44:50 +0000 Subject: [PATCH 5/9] Added tested for malformed root and sub-assets, and "sub-asset is not requested type". --- crates/bevy_asset/src/lib.rs | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index 504e7b83f6332..911c6098edf81 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -740,7 +740,7 @@ mod tests { sync::Mutex, }; use bevy_reflect::TypePath; - use core::time::Duration; + use core::{any::TypeId, time::Duration}; use futures_lite::AsyncReadExt; use serde::{Deserialize, Serialize}; use std::path::{Path, PathBuf}; @@ -2919,8 +2919,13 @@ mod tests { // A simplified subset of `AssetLoadError` for easier comparison. #[derive(Debug, PartialEq, Eq)] enum TestAssetLoadError { + RequestedHandleTypeMismatch { + requested: TypeId, + actual_asset_name: &'static str, + }, MissingAssetLoader, AssetReaderErrorNotFound, + AssetLoaderError, MissingLabel, } @@ -2938,10 +2943,19 @@ mod tests { impl From<&AssetLoadError> for TestAssetLoadError { fn from(value: &AssetLoadError) -> TestAssetLoadError { match value { + AssetLoadError::RequestedHandleTypeMismatch { + requested, + actual_asset_name, + .. + } => Self::RequestedHandleTypeMismatch { + requested: *requested, + actual_asset_name, + }, AssetLoadError::MissingAssetLoader { .. } => Self::MissingAssetLoader, AssetLoadError::AssetReaderError(AssetReaderError::NotFound(_)) => { Self::AssetReaderErrorNotFound } + AssetLoadError::AssetLoaderError { .. } => Self::AssetLoaderError, AssetLoadError::MissingLabel { .. } => Self::MissingLabel, _ => todo!("{:?}", value), } @@ -2976,6 +2990,8 @@ mod tests { )"#, ); + dir.insert_asset_text(Path::new("malformed.cool.ron"), "MALFORMED"); + let asset_server = app.world().resource::().clone(); let handle = asset_server.load::(path); let mut load_state = LoadState::NotLoaded; @@ -3007,6 +3023,7 @@ mod tests { "does_not_exist.cool.ron", TestLoadState::Failed(TestAssetLoadError::AssetReaderErrorNotFound), ); + test_load_state::( "sub-asset exists", "test.cool.ron#subasset", @@ -3020,8 +3037,23 @@ mod tests { ); test_load_state::( - "sub-asset exists but is wrong type", + "sub-asset is not requested type", "test.cool.ron#subasset", + TestLoadState::Failed(TestAssetLoadError::RequestedHandleTypeMismatch { + requested: TypeId::of::(), + actual_asset_name: "bevy_asset::tests::SubText", + }), + ); + + test_load_state::( + "malformed root asset", + "malformed.cool.ron", + TestLoadState::Failed(TestAssetLoadError::AssetLoaderError), + ); + + test_load_state::( + "sub-asset of malformed root asset", + "malformed.cool.ron#subasset", // XXX TODO: This is broken. TestLoadState::Loading, ); From 35b6c0b68d751fcd16662c61194595bf82421f92 Mon Sep 17 00:00:00 2001 From: Greeble <166992735+greeble-dev@users.noreply.github.com> Date: Tue, 20 Jan 2026 15:47:50 +0000 Subject: [PATCH 6/9] Added "sub-asset of root asset that does not exist" test. --- crates/bevy_asset/src/lib.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index 911c6098edf81..8010771adbdfc 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -3018,16 +3018,22 @@ mod tests { fn load_fail() { test_load_state::("root asset exists", "test.cool.ron", TestLoadState::Loaded); + test_load_state::( + "sub-asset exists", + "test.cool.ron#subasset", + TestLoadState::Loaded, + ); + test_load_state::( "root asset does not exist", "does_not_exist.cool.ron", TestLoadState::Failed(TestAssetLoadError::AssetReaderErrorNotFound), ); - test_load_state::( - "sub-asset exists", - "test.cool.ron#subasset", - TestLoadState::Loaded, + test_load_state::( + "sub-asset of root asset that does not exist", + "does_not_exist.cool.ron#subasset", + TestLoadState::Failed(TestAssetLoadError::AssetReaderErrorNotFound), ); test_load_state::( From 795363a1bad007c4b729cc12233f4d37164d2c7c Mon Sep 17 00:00:00 2001 From: Greeble <166992735+greeble-dev@users.noreply.github.com> Date: Wed, 21 Jan 2026 14:17:17 +0000 Subject: [PATCH 7/9] Improved legibility when `TestAssetLoadError` is lacking support for a `AssetLoadError` variant. --- crates/bevy_asset/src/lib.rs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index 8010771adbdfc..31ee623543a80 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -2929,13 +2929,13 @@ mod tests { MissingLabel, } - impl From<&LoadState> for TestLoadState { - fn from(value: &LoadState) -> Self { + impl From for TestLoadState { + fn from(value: LoadState) -> Self { match value { LoadState::NotLoaded => Self::NotLoaded, LoadState::Loading => Self::Loading, LoadState::Loaded => Self::Loaded, - LoadState::Failed(err) => Self::Failed((&**err).into()), + LoadState::Failed(err) => Self::Failed((&*err).into()), } } } @@ -2957,12 +2957,12 @@ mod tests { } AssetLoadError::AssetLoaderError { .. } => Self::AssetLoaderError, AssetLoadError::MissingLabel { .. } => Self::MissingLabel, - _ => todo!("{:?}", value), + _ => panic!("TestAssetLoadError's From<&AssetLoaderError> is missing a case for AssetLoadError \"{:?}\".", value), } } } - // An asset type that's registered but doesn't have a loader. + // An asset type that doesn't have a registered loader. #[derive(Asset, TypePath)] struct LoaderlessAsset; @@ -2994,19 +2994,18 @@ mod tests { let asset_server = app.world().resource::().clone(); let handle = asset_server.load::(path); - let mut load_state = LoadState::NotLoaded; + let mut load_state = TestLoadState::NotLoaded; - // XXX TODO: Is there a way to know that the load state is final? for _ in 0..LARGE_ITERATION_COUNT { app.update(); - load_state = asset_server.get_load_state(&handle).unwrap(); - if TestLoadState::from(&load_state) == expect_load_state { + load_state = asset_server.get_load_state(&handle).unwrap().into(); + if load_state == expect_load_state { break; } } assert!( - TestLoadState::from(&load_state) == expect_load_state, + load_state == expect_load_state, "For test \"{}\", expected {:?} but got {:?}.", label, expect_load_state, @@ -3014,8 +3013,10 @@ mod tests { ); } + // Tests that `AssetServer::get_load_state` returns the correct state after + // various loads, some of which trigger errors. #[test] - fn load_fail() { + fn load_failure() { test_load_state::("root asset exists", "test.cool.ron", TestLoadState::Loaded); test_load_state::( From 6aedd518a607512af572c9dcba46f73049cdc2cf Mon Sep 17 00:00:00 2001 From: Greeble <166992735+greeble-dev@users.noreply.github.com> Date: Wed, 21 Jan 2026 14:34:06 +0000 Subject: [PATCH 8/9] Tidy ups. --- crates/bevy_asset/src/lib.rs | 11 ++++++----- crates/bevy_asset/src/server/mod.rs | 2 ++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index 31ee623543a80..31d9c376b566b 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -2966,11 +2966,12 @@ mod tests { #[derive(Asset, TypePath)] struct LoaderlessAsset; - // Load the given path and check that the eventual load state matches. + // Load the given path and test that `AssetServer::get_load_state` returns + // the given state. fn test_load_state( label: &'static str, path: &'static str, - expect_load_state: TestLoadState, + expected_load_state: TestLoadState, ) { let (mut app, dir) = create_app(); @@ -2999,16 +3000,16 @@ mod tests { for _ in 0..LARGE_ITERATION_COUNT { app.update(); load_state = asset_server.get_load_state(&handle).unwrap().into(); - if load_state == expect_load_state { + if load_state == expected_load_state { break; } } assert!( - load_state == expect_load_state, + load_state == expected_load_state, "For test \"{}\", expected {:?} but got {:?}.", label, - expect_load_state, + expected_load_state, load_state, ); } diff --git a/crates/bevy_asset/src/server/mod.rs b/crates/bevy_asset/src/server/mod.rs index cbd889b249456..82dce8534cabc 100644 --- a/crates/bevy_asset/src/server/mod.rs +++ b/crates/bevy_asset/src/server/mod.rs @@ -840,6 +840,8 @@ impl AssetServer { let final_handle = if let Some(label) = path.label_cow() { match loaded_asset.labeled_assets.get(&label) { Some(labeled_asset) => { + // If we know the requested type then check it + // matches the labeled asset. if let Some(asset_id) = asset_id && asset_id.type_id != labeled_asset.handle.type_id() { From 51f8ae3e1a061f30978ff9cbcc9008e90add8994 Mon Sep 17 00:00:00 2001 From: Greeble <166992735+greeble-dev@users.noreply.github.com> Date: Wed, 21 Jan 2026 15:20:56 +0000 Subject: [PATCH 9/9] Fixed loader error events getting sent to the root asset when the load was requested on the sub-asset. This meant the `LoadStatus` of the sub-asset would not reflect the error. --- crates/bevy_asset/src/lib.rs | 3 +-- crates/bevy_asset/src/server/mod.rs | 12 +++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index 31d9c376b566b..4cdfd35005096 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -3062,8 +3062,7 @@ mod tests { test_load_state::( "sub-asset of malformed root asset", "malformed.cool.ron#subasset", - // XXX TODO: This is broken. - TestLoadState::Loading, + TestLoadState::Failed(TestAssetLoadError::AssetLoaderError), ); test_load_state::( diff --git a/crates/bevy_asset/src/server/mod.rs b/crates/bevy_asset/src/server/mod.rs index 82dce8534cabc..92d0cfa677323 100644 --- a/crates/bevy_asset/src/server/mod.rs +++ b/crates/bevy_asset/src/server/mod.rs @@ -893,11 +893,13 @@ impl AssetServer { Ok(final_handle) } Err(err) => { - self.send_asset_event(InternalAssetEvent::Failed { - index: base_asset_id, - error: err.clone(), - path: path.into_owned(), - }); + if let Some(asset_id) = asset_id { + self.send_asset_event(InternalAssetEvent::Failed { + index: asset_id, + error: err.clone(), + path: path.into_owned(), + }); + } Err(err) } }