bevyengine/bevy · error · IntoDynamicImageError

Failed to convert into {0:?}.

Error message

Failed to convert into {0:?}.

What it means

IntoDynamicImageError::UnknownConversionError is returned at the tail of Image::try_into_dynamic (image_texture_conversion.rs:189-191) when the format WAS one of the supported ones but ImageBuffer::from_raw returned None. from_raw fails exactly when data.len() != width * height * bytes_per_pixel_of_variant — i.e. the CPU buffer size does not match the image's declared Extent3d. So this variant means internal inconsistency between image.data, image.texture_descriptor.size, and the format, not an unsupported format.

Source

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

            // Throw and error if conversion isn't supported
            texture_format => return Err(IntoDynamicImageError::UnsupportedFormat(texture_format)),
        }
        .ok_or(IntoDynamicImageError::UnknownConversionError(
            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]));

View on GitHub (pinned to 396ca72708)

Solutions

  1. Recompute and verify the invariant before converting: data.len() == width * height * pixel_bytes for the format (R8Unorm=1, Rg8Unorm=2, Rgba8UnormSrgb=4).
  2. Prefer constructing images through Image::new(Extent3d{...}, dimension, data, format, usage), which sets size and data together.
  3. If you changed data length, update texture_descriptor.size (or recreate the Image) in the same operation.
  4. Log the expected-vs-actual byte counts when the error fires to find which dimension is stale.

Example fix

// before — size field is stale after data was resized
image.texture_descriptor.size = Extent3d { width: 256, height: 256, depth_or_array_layers: 1 };
image.data = Some(vec![0u8; 250 * 250 * 4]);
let dyn_img = image.try_into_dynamic()?; // Err(UnknownConversionError(Rgba8UnormSrgb))

// after — keep data and extent in sync
let w = 250u32;
let h = 250u32;
let image = Image::new(
    Extent3d { width: w, height: h, depth_or_array_layers: 1 },
    TextureDimension::D2,
    vec![0u8; (w * h * 4) as usize],
    TextureFormat::Rgba8UnormSrgb,
    RenderAssetUsages::default(),
);
let dyn_img = image.try_into_dynamic()?;
Defensive patterns

Strategy: validation

Validate before calling

// check the byte-count invariant before converting
let (w, h) = (image.width(), image.height());
let bpp = match image.texture_descriptor.format {
    TextureFormat::R8Unorm => 1,
    TextureFormat::Rg8Unorm => 2,
    _ => 4,
};
debug_assert_eq!(image.data.as_ref().map(|d| d.len()), Some((w * h * bpp) as usize));

Type guard

fn buffer_consistent(image: &Image) -> bool {
    let bpp = match image.texture_descriptor.format {
        TextureFormat::R8Unorm => 1,
        TextureFormat::Rg8Unorm => 2,
        _ => 4,
    };
    image.data.as_ref().is_some_and(|d| {
        d.len() == (image.width() * image.height() * bpp) as usize
    })
}

Try / catch

match image.try_into_dynamic() {
    Err(IntoDynamicImageError::UnknownConversionError(fmt)) => {
        // data.len() != w*h*bpp: rebuild the image with matching extent+data via Image::new
    }
    result => result,
}

Prevention

When it happens

Trigger: Calling try_into_dynamic on an Image assembled by hand with a size field that does not match the data length (e.g. data resized or truncated without updating texture_descriptor.size); mutating image.data in place while keeping stale dimensions; formats where pixel size assumptions differ from what was stored (Rg8Unorm image fed 4-byte-per-pixel data).

Common situations: Manual Image construction for procedural generation/tests where width/height were edited; code copying pixel buffers between images of different sizes; off-by-one row padding when preparing data on the CPU.

Related errors


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