bevyengine/bevy · error · ImageLoaderError

Invalid array layout: {0}

Error message

Invalid array layout: {0}

What it means

ImageLoaderError::ArrayLayout (image_loader.rs:185-186) wraps TextureReinterpretationError, raised when ImageLoaderSettings::array_layout reinterprets a stacked 2D image as a texture array (image_loader.rs:245-265). The inner variants (image.rs:2200-2246) name the exact problem: WrongDimension (not a 2D image), InvalidLayerCount (already layered), HeightNotDivisibleByLayers, GridHeightNotDivisibleByTileHeight/GridWidthNotDivisibleByTileWidth, NotEnoughLayers, IncompatibleSizes, and InvalidTextureFormat ("Is it compressed?"). It fires after a successful decode, while slicing the pixel data into layers.

Source

Thrown at crates/bevy_image/src/image_loader.rs:185

            sampler: ImageSampler::Default,
            asset_usage: RenderAssetUsages::default(),
            array_layout: None,
        }
    }
}

/// An error when loading an image using [`ImageLoader`].
#[non_exhaustive]
#[derive(Debug, Error)]
pub enum ImageLoaderError {
    /// An error occurred while trying to load the image bytes.
    #[error("Failed to load image bytes: {0}")]
    Io(#[from] std::io::Error),
    /// An error occurred while trying to decode the image bytes.
    #[error("Could not load texture file: {0}")]
    FileTexture(#[from] FileTextureError),
    /// An error occurred while trying to interpret the image bytes as an array texture.
    #[error("Invalid array layout: {0}")]
    ArrayLayout(#[from] TextureReinterpretationError),
}

impl AssetLoader for ImageLoader {
    type Asset = Image;
    type Settings = ImageLoaderSettings;
    type Error = ImageLoaderError;
    async fn load(
        &self,
        reader: &mut dyn Reader,
        settings: &ImageLoaderSettings,
        load_context: &mut LoadContext<'_>,
    ) -> Result<Image, Self::Error> {
        let mut bytes = Vec::new();
        reader.read_to_end(&mut bytes).await?;
        let image_type = match settings.format {
            ImageFormatSetting::FromExtension => {
                // use the file extension for the image type

View on GitHub (pinned to 396ca72708)

Solutions

  1. Make the sheet geometry exact: height must be rows * row_height and the grid must divide evenly into tiles (verify width % tile_w == 0 and height % tile_h == 0).
  2. Double-check the RowCount/RowHeight/GridCount/GridSize settings — swapped rows/columns is the most common mistake.
  3. Ensure the source image is a plain single-layer 2D uncompressed image (PNG), not an already-layered or compressed file.
  4. For RowHeight/GridSize, remember Bevy divides image.height() by the value you give — pick the number that yields an integer layer count >= 2 (NotEnoughLayers otherwise).

Example fix

// before — sheet is 256x252, rows = 4 (252 is not divisible)
// textures/particles.meta: "loader": { "settings": { "array_layout": { "type": "row_count", "rows": 4 } } }

// after — author the sheet at an exact multiple (4 * 64 = 256) and keep the same meta
// re-export particles.png at 256x256
Defensive patterns

Strategy: validation

Validate before calling

// verify the sheet divides evenly before enabling array_layout
let (w, h) = (image.width(), image.height());
let layers = h / row_height;
assert!(h % row_height == 0 && layers >= 2, "height {h} not divisible by {row_height}");
assert!(w % tile_w == 0 && h % tile_h == 0, "grid must divide evenly");

Type guard

fn array_layout_ok(image: &Image, rows: u32) -> bool {
    image.texture_descriptor.dimension == TextureDimension::D2
        && image.texture_descriptor.size.depth_or_array_layers == 1
        && image.height() % rows == 0
        && image.height() / rows >= 2
        && !image.is_compressed()
}

Try / catch

// array_layout is loader-side; failures arrive via events:
if let ImageLoaderError::ArrayLayout(e) = &*ev.error {
    error!("array reinterpretation failed: {e}"); // fix sheet dimensions or meta settings
}

Prevention

When it happens

Trigger: Setting array_layout in an image's .meta or via load_with_settings: RowCount { rows } on a sheet whose height is not divisible by rows; RowHeight/GridCount on a grid that does not tile evenly (image_loader.rs:248-264); an already-layered or 3D source; a compressed source format (InvalidTextureFormat).

Common situations: Texture2DArray sprite sheets authored with off-by-one pixel sizes (e.g. 256x252 for 4 rows of 64); grid layouts declared with swapped columns/rows; applying array_layout meta to files that were already exported as arrays.

Related errors


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