bevyengine/bevy · error · TextureAccessError

unsupported texture format: {0:?}

Error message

unsupported texture format: {0:?}

What it means

TextureAccessError::UnsupportedTextureFormat is returned by bevy_image's CPU pixel accessors (Image::get_color_at*, set_color_at*, pixel_bytes, pixel_bytes_mut, pixel_data_offset) when the TextureFormat has no fixed per-pixel byte size. The gate is TextureFormat::pixel_size (crates/bevy_image/src/image.rs:2354), which errors for any format whose block dimensions are not (1,1) — every block-compressed format (Bc*, Etc2*, Astc*). The color accessors additionally reject formats that cannot decode into Color, such as non-byte-aligned (Rg11b10-like) and signed-integer formats. Bevy throws it because per-pixel addressing is undefined for block-compressed data.

Source

Thrown at crates/bevy_image/src/image.rs:2272

    /// Most often this is returned when attempting to access pixel data of compressed textures.
    #[error("unsupported texture format: {0:?}")]
    UnsupportedTextureFormat(TextureFormat),
    /// Attempted to access the data of an image before it was initialized, or after it was moved
    /// to the GPU.
    ///
    /// See [`RenderAssetUsages`] for more information about when an asset's data is moved, and
    /// how to retain it if necessary.
    #[error("image data is not initialized")]
    Uninitialized,
    /// The texture's dimension was different than indicated by the accessor used.
    #[error("attempt to access texture with different dimension")]
    WrongDimension,
}

/// An error that occurs when loading a texture.
#[derive(Error, Debug)]
pub enum TextureError {
    /// Image MIME type is invalid.
    #[error("invalid image mime type: {0}")]
    InvalidImageMimeType(String),
    /// Image extension is invalid.
    #[error("invalid image extension: {0}")]
    InvalidImageExtension(String),
    /// Failed to load an image.
    #[error("failed to load an image: {0}")]
    ImageError(#[from] image::ImageError),
    /// Texture format isn't supported.
    #[error("unsupported texture format: {0}")]
    UnsupportedTextureFormat(String),
    /// Supercompression isn't supported.
    #[error("supercompression not supported: {0}")]
    SuperCompressionNotSupported(String),
    /// Failed to decompress an image.
    #[error("failed to decompress an image: {0}")]
    SuperDecompressionError(String),
    /// Invalid data.

View on GitHub (pinned to 221e52ae32)

Solutions

  1. Keep an uncompressed companion asset (e.g. the source PNG) for any texture you must read or write on the CPU, and do pixel access only on that copy.
  2. Check image.is_compressed() or image.texture_descriptor.format.pixel_size().is_err() before every accessor call and skip/log instead of unwrapping.
  3. If you control the pipeline, export the texture uncompressed (PNG/Rgba8UnormSrgb) instead of BC/ETC2/ASTC.
  4. As a last resort, decode block-compressed data yourself with a CPU BC/ETC2/ASTC decoder crate before doing byte-level access.

Example fix

// before — image came from a .ktx2, format is Bc7RgbaUnormSrgb
let color = image.get_color_at(10, 20)?; // Err(UnsupportedTextureFormat(Bc7RgbaUnormSrgb))

// after — read from the uncompressed source copy you shipped next to it
let color = if image.is_compressed() {
    uncompressed_source.get_color_at(10, 20)?
} else {
    image.get_color_at(10, 20)?
};
Defensive patterns

Strategy: validation

Validate before calling

use bevy_image::{Image, TextureFormatPixelInfo};

fn can_read_pixels(image: &Image) -> bool {
    !image.is_compressed() && image.texture_descriptor.format.pixel_size().is_ok()
}

// before any accessor:
if can_read_pixels(&image) {
    let color = image.get_color_at(x, y)?;
}

Type guard

fn pixel_readable(image: &Image) -> bool {
    use bevy_image::TextureFormatPixelInfo;
    !image.is_compressed()
        && image.texture_descriptor.format.pixel_size().is_ok()
        && image.data.is_some()
}

Try / catch

match image.get_color_at(x, y) {
    Ok(color) => { /* use color */ }
    Err(TextureAccessError::UnsupportedTextureFormat(fmt)) => {
        warn!("no CPU pixel access for compressed format {fmt:?}");
    }
    Err(TextureAccessError::Uninitialized) => { /* data is GPU-only */ }
    Err(TextureAccessError::OutOfBounds { x, y, z }) => { /* clamp coords */ }
    Err(TextureAccessError::WrongDimension) => { /* wrong accessor */ }
}

Prevention

When it happens

Trigger: Calling image.get_color_at(x, y) / get_color_at_1d / get_color_at_3d / set_color_at* on an Image whose texture_descriptor.format is block-compressed (e.g. Bc1RgbaUnorm loaded from a .dds/.ktx2), or calling pixel_bytes/pixel_bytes_mut/Image::convert on such an image. The crate's own test (image.rs:2528, compressed_texture_format_is_reported_correctly) shows get_color_at on Bc1RgbaUnorm returning exactly this variant.

Common situations: Loading a compressed .ktx2/.dds/.basis texture and reading pixels for a minimap, picking, or runtime atlas packing; calling Image::convert on GPU-compressed data; indexing pixel data of an image asset after the ImageLoader selected a BC/ASTC format supported by the adapter.

Related errors


AI-assisted analysis of bevyengine/bevy@221e52ae32 (2026-08-20). Data as JSON: /api/errors/1bdf569f99b80fc2. Report an issue: GitHub.