bevyengine/bevy · error · PrepareAssetError

Failed to build bind group: {0}

Error message

Failed to build bind group: {0}

What it means

`PrepareAssetError::AsBindGroupError(AsBindGroupError)` is the hard-failure variant of render-asset preparation: building the bind group for an `AsBindGroup` material failed. The prepare system logs it via `error!` ("{type} Bind group construction failed: {e}") and the asset is skipped for that frame, so the material does not render until re-prepared.

Source

Thrown at crates/bevy_render/src/erased_render_asset.rs:24

use bevy_asset::RenderAssetUsages;
use bevy_asset::{Asset, AssetEvent, AssetId, Assets, UntypedAssetId};
use bevy_ecs::{
    prelude::{Commands, IntoScheduleConfigs, Local, MessageReader, ResMut, Resource},
    schedule::{ScheduleConfigs, SystemSet},
    system::{ScheduleSystem, StaticSystemParam, SystemParam, SystemParamItem, SystemState},
    world::{FromWorld, Mut},
};
use bevy_log::{debug, error};
use bevy_platform::collections::{HashMap, HashSet};
use bevy_render::render_asset::RenderAssetBytesPerFrameLimiter;
use core::marker::PhantomData;
use thiserror::Error;

#[derive(Debug, Error)]
pub enum PrepareAssetError<E: Send + Sync + 'static> {
    #[error("Failed to prepare asset")]
    RetryNextUpdate(E),
    #[error("Failed to build bind group: {0}")]
    AsBindGroupError(AsBindGroupError),
}

/// The system set during which we extract modified assets to the render world.
#[derive(SystemSet, Clone, PartialEq, Eq, Debug, Hash)]
pub struct AssetExtractionSystems;

/// Describes how an asset gets extracted and prepared for rendering.
///
/// In the [`ExtractSchedule`] step the [`ErasedRenderAsset::SourceAsset`] is transferred
/// from the "main world" into the "render world".
///
/// After that in the [`RenderSystems::PrepareAssets`] step the extracted asset
/// is transformed into its GPU-representation of type [`ErasedRenderAsset`].
pub trait ErasedRenderAsset: Send + Sync + 'static {
    /// The representation of the asset in the "main world".
    type SourceAsset: Asset + Clone;
    /// The target representation of the asset in the "render world".

View on GitHub (pinned to 396ca72708)

Solutions

  1. Read the nested AsBindGroupError in the log line — it names the exact binding and mismatch
  2. Make the `#[texture(dimension = ...)]` attributes match the actual textures you assign at runtime
  3. Match sampler filtering to texture filterability (depth/unfilterable textures need non-filtering samplers) and check required device features

Example fix

// before
#[texture(0, dimension = "2D")]
#[sampler(1)]
env_map: Option<Handle<Image>>, // actually assigned a cubemap

// after
#[texture(0, dimension = "Cubemap")]
#[sampler(1)]
env_map: Option<Handle<Image>>,
Defensive patterns

Strategy: try-catch

Validate before calling

// before relying on a material, dry-run its bind group inputs
let layout_ok = device.limits().max_bind_groups >= expected_bindings;
assert!(layout_ok, "device cannot host this material layout");

Try / catch

// when implementing or wrapping as_bind_group for custom materials
match material.as_bind_group(&layout, render_device, images) {
    Ok(bind_group) => { /* use */ }
    Err(AsBindGroupError::RetryNextUpdate) => { /* wait for next frame */ }
    Err(e) => error!("material bind group failed: {e:?}"),
}

Prevention

When it happens

Trigger: A material's `as_bind_group` errors during preparation: texture view/sample type not matching the declared `#[texture]` attributes (wrong dimension, non-filterable texture with a filtering sampler), or a binding not buildable on the current device.

Common situations: Declaring `dimension = "2D"` but assigning a cubemap/3D texture; using a filtering sampler on depth/unfilterable formats; bindless-required bindings on hardware without bindless support.

Related errors


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