bevyengine/bevy · error
B0003
B0003
Error message
error[B0003]: Could not insert a bundle (of type `{}`) for entity {first_entity} because: {err}. See: https://bevyengine.org/learn/errors/b0003 What it means
This is Bevy error B0003, raised inside World::insert_batch_with_caller, which backs World::insert_batch and World::insert_batch_if_new. It panics when the FIRST entity of the batch cannot be resolved by Entities::get_spawned, i.e. the Entity id is not a currently spawned (alive) entity of this World. Because batch inserts skip per-entity error handling for speed, any dead or foreign id aborts with this error and a link to https://bevyengine.org/learn/errors/b0003.
Source
Thrown at crates/bevy_ecs/src/world/mod.rs:2521
) where
I: IntoIterator,
I::IntoIter: Iterator<Item = (Entity, B)>,
B: Bundle<Effect: NoBundleEffect>,
{
struct InserterArchetypeCache<'w> {
inserter: BundleInserter<'w>,
archetype_id: ArchetypeId,
}
let change_tick = self.change_tick();
let bundle_id = self.register_bundle_info::<B>();
let mut batch_iter = batch.into_iter();
if let Some((first_entity, first_bundle)) = batch_iter.next() {
match self.entities().get_spawned(first_entity) {
Err(err) => {
panic!("error[B0003]: Could not insert a bundle (of type `{}`) for entity {first_entity} because: {err}. See: https://bevyengine.org/learn/errors/b0003", core::any::type_name::<B>());
}
Ok(first_location) => {
let mut cache = InserterArchetypeCache {
// SAFETY: we initialized this bundle_id in `register_info`
inserter: unsafe {
BundleInserter::new_with_id(
self,
first_location.archetype_id,
bundle_id,
change_tick,
)
},
archetype_id: first_location.archetype_id,
};
move_as_ptr!(first_bundle);
// SAFETY: `entity` is valid, `location` matches entity, bundle matches inserter, B::Effect: NoBundleEffect
unsafe {
cache.inserter.insert(View on GitHub (pinned to 396ca72708)
Solutions
- Switch to the fallible API world.try_insert_batch(batch) or try_insert_batch_if_new(batch) and handle the returned TryInsertBatchError.
- Filter the batch before inserting: retain only entities for which world.entities().contains(e) (or get_spawned(e).is_ok()) is true.
- Re-check ownership/lifecycle: stop holding Entity ids past the despawn point, or verify with world.get_entity_mut(e) before building the batch.
- If ids come from another World, remap them (e.g. via the SubEntity/MainEntity mapping in bevy_extract) before batch-inserting.
Example fix
// before
world.insert_batch(batch); // panics if any entity was despawned
// after
let alive: Vec<(Entity, MyBundle)> = batch
.into_iter()
.filter(|(e, _)| world.entities().contains(*e))
.collect();
world.insert_batch(alive);
// or use the fallible version:
if let Err(e) = world.try_insert_batch(batch) {
warn!("batch insert skipped: {e}");
} Defensive patterns
Strategy: validation
Validate before calling
let batch: Vec<(Entity, B)> = batch
.into_iter()
.filter(|(e, _)| world.entities().contains(*e))
.collect();
world.insert_batch(batch); Try / catch
match world.try_insert_batch(batch) {
Ok(()) => {}
Err(TryInsertBatchError::EntityDoesNotExist(e)) => warn!("dead entity in batch: {e:?}"),
} Prevention
- Prefer try_insert_batch / try_insert_batch_if_new whenever entity lifetimes are not fully controlled.
- Validate entity ids with world.entities().contains immediately before batch insertion, not at batch-construction time.
- Never reuse Entity ids across Worlds; remap via MainEntity/SubEntity in extraction setups.
When it happens
Trigger: Passing an (Entity, Bundle) pair to world.insert_batch(...) / insert_batch_if_new(...) where the Entity was already despawned, was obtained from a different World (e.g. the render sub-world vs the main world), or is a stale id held across frames after the entity was deleted by another system.
Common situations: Buffering Entity ids in a Vec or event for later batch insertion while another system (or an observer / hook) despawns those entities in between; multi-world setups (extraction) mixing up main-world and sub-world Entity ids; commands deferred across a frame boundary so the target entity is gone by execution time.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- Cannot call `ReflectComponent::reflect_mut` on component {na
- Cannot call `ReflectComponent::reflect_unchecked_mut` on com
- Resource entity {} of {} has been despawned, when it's not s
- System with key {:?} does not exist in the schedule
- System set with key {:?} does not exist in the schedule
AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20).
Data as JSON: /api/errors/2628167e0eac52ec.
Report an issue: GitHub.