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

  1. Update Bevy; check the issue tracker for font registration / get_mut_untracked panics
  2. Audit custom code that removes Font assets or invokes the registration system outside the normal schedule
  3. 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

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


AI-assisted analysis of bevyengine/bevy@4805ca792c (2026-08-20). Data as JSON: /api/errors/d6906c1cbf6ec363. Report an issue: GitHub.