bevyengine/bevy · error

Field name should exist

Error message

Field name should exist

What it means

ImageSaver (the AssetSaver for bevy Image assets) refuses to guess an output encoding when it cannot deduce one. With the default SaveImageFormatSetting::FromExtension, the saver looks at the destination AssetPath's extension to pick the file format; if the path has none, saving aborts before any bytes are written and the offending path is embedded in the error.

Source

Thrown at crates/bevy_animation/src/animation_curves.rs:255

    }

    fn evaluator_id(&self) -> EvaluatorId<'_> {
        EvaluatorId::ComponentField(&self.evaluator_id)
    }
}

impl<C: Typed, P, F: Fn(&mut C) -> &mut P + 'static> AnimatedField<C, P, F> {
    /// Creates a new instance of [`AnimatedField`]. This operates under the assumption that
    /// `C` is a reflect-able struct, and that `field_name` is a valid field on that struct.
    ///
    /// # Panics
    /// If the type of `C` is not a struct or if the `field_name` does not exist.
    pub fn new_unchecked(field_name: &str, func: F) -> Self {
        let field_index;
        if let TypeInfo::Struct(struct_info) = C::type_info() {
            field_index = struct_info
                .index_of(field_name)
                .expect("Field name should exist");
        } else if let TypeInfo::TupleStruct(struct_info) = C::type_info() {
            field_index = field_name
                .parse()
                .expect("Field name should be a valid tuple index");
            if field_index >= struct_info.field_len() {
                panic!("Field name should be a valid tuple index");
            }
        } else {
            panic!("Only structs are supported in `AnimatedField::new_unchecked`")
        }

        Self {
            func,
            evaluator_id: Hashed::new((TypeId::of::<C>(), field_index)),
            marker: PhantomData,
        }
    }
}

View on GitHub (pinned to 396ca72708)

Solutions

  1. Append a supported extension such as .png to the destination asset path
  2. Set an explicit format instead of relying on the path: ImageSaverSettings { format: SaveImageFormatSetting::Format(ImageFormat::Png) }
  3. If paths are built at runtime, debug_assert!(path.extension().is_some()) before invoking the saver

Example fix

// before
let path = AssetPath::from_path(Path::new("screenshots/frame_42"));
save_using_saver(server, &ImageSaver, path, saved, &ImageSaverSettings::default()).await?;

// after
let path = AssetPath::from_path(Path::new("screenshots/frame_42.png"));
save_using_saver(server, &ImageSaver, path, saved, &ImageSaverSettings::default()).await?;
Defensive patterns

Strategy: validation

Validate before calling

use bevy_asset::AssetPath;
use std::path::Path;

fn ensure_extension(path: &Path) -> Result<&Path, String> {
    path.extension()
        .map(|_| path)
        .ok_or_else(|| format!("destination '{:?}' has no extension; use e.g. .png", path))
}

Type guard

fn has_extension(path: &AssetPath) -> bool {
    path.get_extension().is_some()
}

Try / catch

match save_using_saver(server, &ImageSaver, path, saved, &settings).await {
    Err(SaveImageError::MissingExtension(p)) => warn!("skipping save, no extension: {p}"),
    other => other?,
}

Prevention

When it happens

Trigger: Calling save_using_saver (or an asset-processing pipeline) with ImageSaver and ImageSaverSettings::default() while the destination path has no '.' segment, e.g. AssetPath::from_path(Path::new("images/player")). At crates/bevy_image/src/saver.rs:38-39, asset_path.get_extension() returns None and MissingExtension is returned.

Common situations: Saving runtime-generated screenshots or procedural textures to computed paths ("screenshot_3", "output/texture_v1") and forgetting the .png suffix; renaming asset files without extensions; refactoring code that used to pass an explicit format.

Related errors


AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20). Data as JSON: /api/errors/2b8884b5890873df. Report an issue: GitHub.