bevyengine/bevy · error · IntoDynamicImageError

Image has no texture data

Error message

Image has no texture data

What it means

IntoDynamicImageError::UninitializedImage is returned at the top of Image::try_into_dynamic (image_texture_conversion.rs:161-163) when self.data is None — the Image has no CPU-side bytes. This is the conversion-API twin of TextureAccessError::Uninitialized: the asset was loaded with RenderAssetUsages::RENDER_WORLD (CPU copy dropped after GPU upload) or otherwise constructed without data (Image::default / new_uninit). Bevy cannot produce a DynamicImage from texture bytes it no longer holds.

Source

Thrown at crates/bevy_image/src/image_texture_conversion.rs:208

            self.texture_descriptor.format,
        ))
    }
}

/// Errors that occur while converting an [`Image`] into a [`DynamicImage`]
#[non_exhaustive]
#[derive(Error, Debug)]
pub enum IntoDynamicImageError {
    /// Conversion into dynamic image not supported for source format.
    #[error("Conversion into dynamic image not supported for {0:?}.")]
    UnsupportedFormat(TextureFormat),

    /// Encountered an unknown error during conversion.
    #[error("Failed to convert into {0:?}.")]
    UnknownConversionError(TextureFormat),

    /// Tried to convert an image that has no texture data
    #[error("Image has no texture data")]
    UninitializedImage,
}

#[cfg(test)]
mod test {
    use image::{GenericImage, Rgba};

    use super::*;

    #[test]
    fn two_way_conversion() {
        // Check to see if color is preserved through an rgba8 conversion and back.
        let mut initial = DynamicImage::new_rgba8(1, 1);
        initial.put_pixel(0, 0, Rgba::from([132, 3, 7, 200]));

        let image = Image::from_dynamic(initial.clone(), true, RenderAssetUsages::RENDER_WORLD);

        // NOTE: Fails if `is_srgb = false` or the dynamic image is of the type rgb8.

View on GitHub (pinned to 396ca72708)

Solutions

  1. Load/retain the texture with RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD (default) so data survives on CPU.
  2. Check image.data.is_some() before calling try_into_dynamic and report a precise error.
  3. If the image is RENDER_WORLD-only, re-request the original asset from the AssetServer instead of the GPU-side handle.
  4. Never construct images you intend to convert with Image::default() or new_uninit without filling data.

Example fix

// before
server.load_with_settings("photo.png", |s: &mut ImageLoaderSettings| {
    s.asset_usage = RenderAssetUsages::RENDER_WORLD;
});
let dyn_img = image.try_into_dynamic()?; // Err(UninitializedImage)

// after — keep MAIN_WORLD so data stays on CPU
s.asset_usage = RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD;
Defensive patterns

Strategy: validation

Validate before calling

// guard before converting
if image.data.is_none() {
    warn!("image has no CPU data (RENDER_WORLD-only?); cannot convert");
    return;
}
let dyn_img = image.try_into_dynamic()?;

Type guard

fn convertible_now(image: &Image) -> bool {
    image.data.is_some()
        && matches!(
            image.texture_descriptor.format,
            TextureFormat::R8Unorm
                | TextureFormat::Rg8Unorm
                | TextureFormat::Rgba8UnormSrgb
                | TextureFormat::Bgra8UnormSrgb
                | TextureFormat::Bgra8Unorm
        )
}

Try / catch

match image.try_into_dynamic() {
    Err(IntoDynamicImageError::UninitializedImage) => {
        // re-load source with MAIN_WORLD usage, then retry
    }
    Err(IntoDynamicImageError::UnsupportedFormat(f)) => { /* convert format first */ }
    Err(IntoDynamicImageError::UnknownConversionError(f)) => { /* fix data/extent mismatch */ }
    Ok(dyn) => { /* ... */ }
}

Prevention

When it happens

Trigger: Calling image.try_into_dynamic() on an image loaded with asset_usage = RenderAssetUsages::RENDER_WORLD; on Image::default(); on any image whose data field is None (e.g. after data was taken out with .data.take()).

Common situations: Screenshot/thumbnail code running on render-target images created RENDER_WORLD-only; memory-optimized builds that drop CPU copies; extracting a DynamicImage from a handle returned by the render pipeline rather than the original asset.

Related errors


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