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
- 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.
- Check image.is_compressed() or image.texture_descriptor.format.pixel_size().is_err() before every accessor call and skip/log instead of unwrapping.
- If you control the pipeline, export the texture uncompressed (PNG/Rgba8UnormSrgb) instead of BC/ETC2/ASTC.
- 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
- Ship an uncompressed PNG companion for every texture you read pixels from at runtime.
- Route all pixel access through one helper that checks is_compressed() first.
- Add a CI test that reads pixel (0,0) from every image asset to catch compressed/uninitialized sources early.
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
- attempt to access texture with different dimension
- unsupported texture format: {0}
- Could not load texture file: {0}
- Error reading image file {path}: {error}.
- Conversion into dynamic image not supported for {0:?}.
AI-assisted analysis of bevyengine/bevy@221e52ae32 (2026-08-20).
Data as JSON: /api/errors/1bdf569f99b80fc2.
Report an issue: GitHub.