bevyengine/bevy · critical
Each AssetId should have a corresponding asset
Error message
Each AssetId should have a corresponding asset
What it means
An expect() panic in the font-registration system (crates/bevy_text/src/font.rs): for every id returned by Assets<Font>::ids() that has not been registered yet, the code calls fonts.get_mut_untracked(id).expect("Each AssetId should have a corresponding asset"). ids() only yields ids of stored assets, so the asset vanishing between enumeration and access violates an internal invariant - this is a Bevy-side bug, not normal error handling.
Source
Thrown at crates/bevy_text/src/font.rs:77
let new_asset_ids: Vec<_> = if font_removed {
// If any font asset has been removed, clear the font collection and queue the remaining fonts to be reinserted into the collection.
font_cx.collection.clear();
loaded_fonts.clear();
loaded_fonts.extend(fonts.ids());
loaded_fonts.iter().copied().collect()
} else {
fonts.ids().filter(|id| loaded_fonts.insert(*id)).collect()
};
if new_asset_ids.is_empty() && !font_removed {
return;
}
let mut new_family_ids = Vec::new();
for asset_id in &new_asset_ids {
let font = fonts
.get_mut_untracked(*asset_id)
.expect("Each AssetId should have a corresponding asset");
font.alias = format!("asset_id:{asset_id:?}");
// Each font is registered twice in Parley's FontContext collection, once under its embedded family name,
// and once under an alias generated from the asset id.
// This to allow look ups by the embedded family name while also ensuring that font asset handles
// accurately resolve to the correct font asset.
new_family_ids.extend(
font_cx
.collection
.register_fonts(font.data.clone(), None)
.iter()
.map(|(family_id, _)| *family_id),
);
font_cx.collection.register_fonts(
font.data.clone(),
Some(FontInfoOverride {View on GitHub (pinned to 4805ca792c)
Solutions
- Update Bevy; check the issue tracker for font registration / get_mut_untracked panics
- Audit custom code that removes Font assets or invokes the registration system outside the normal schedule
- Reproduce with a minimal app and file a Bevy issue including the panic backtrace and any asset manipulation you perform
Defensive patterns
Strategy: validation
Validate before calling
// If you write similar asset-iteration code, avoid expect:
if let Some(font) = fonts.get_mut_untracked(*asset_id) {
font.alias = format!("asset_id:{asset_id:?}");
// ...
} else {
warn!("font asset {asset_id:?} vanished before registration");
} Prevention
- Run systems through the normal schedule only; no manual/re-entrant invocation
- Do not remove Font assets from custom asset hooks mid-frame
- If it fires, capture the backtrace and file a Bevy issue
When it happens
Trigger: Requires Assets<Font> to be mutated (entries removed) while the registration system iterates ids: manually/re-entrantly running the system, an asset plugin removing fonts from another thread mid-frame, or an ecs/assets regression. Ordinary apps cannot trigger it through public APIs.
Common situations: Custom schedulers that run systems manually inside other systems, exotic asset sources, or version regressions after Bevy upgrades. If you see it, it is almost always worth an upstream issue with the backtrace.
Related errors
- Fatal error when processing text: {e}.
- failed to add glyph to newly-created atlas {0:?}
- failed to get scaled glyph image for cache key: {0:?}
- missing texture atlas layout for the font
- missing texture for the font atlas
AI-assisted analysis of bevyengine/bevy@4805ca792c (2026-08-20).
Data as JSON: /api/errors/d6906c1cbf6ec363.
Report an issue: GitHub.