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
- Append a supported extension such as .png to the destination asset path
- Set an explicit format instead of relying on the path: ImageSaverSettings { format: SaveImageFormatSetting::Format(ImageFormat::Png) }
- 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
- Standardize on .png for all runtime-saved images
- Build destination paths from a single helper that always appends the extension
- Log the full AssetPath before saving in debug builds
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
- Field name should be a valid tuple index
- invalid image extension: {0}
- invalid image mime type: {0}
- failed to load an image: {0}
- Union types are not supported yet.
AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20).
Data as JSON: /api/errors/2b8884b5890873df.
Report an issue: GitHub.