bevyengine/bevy · error · AsBindGroupError

CreateBindGroupDirectly

CreateBindGroupDirectly

Error message

Create the bind group via `as_bind_group()` instead

What it means

AsBindGroupError::CreateBindGroupDirectly is returned by AsBindGroup paths other than as_bind_group() for implementations that must be created through as_bind_group() (bind_group.rs:580 documents this contract). It tells the caller the attempted creation route (e.g. the unprepared/allocator path) is not supported for this type and the bind group must come from as_bind_group().

Source

Thrown at crates/bevy_render/src/render_resource/bind_group.rs:644

    fn bind_group_layout_entries(
        render_device: &RenderDevice,
        force_no_bindless: bool,
    ) -> Vec<BindGroupLayoutEntry>
    where
        Self: Sized;

    fn bindless_descriptor() -> Option<BindlessDescriptor> {
        None
    }
}

/// An error that occurs during [`AsBindGroup::as_bind_group`] calls.
#[derive(Debug, Error)]
pub enum AsBindGroupError {
    /// The bind group could not be generated. Try again next frame.
    #[error("The bind group could not be generated")]
    RetryNextUpdate,
    #[error("Create the bind group via `as_bind_group()` instead")]
    CreateBindGroupDirectly,
    #[error("At binding index {0}, the provided image sampler `{1}` does not match the required sampler type(s) `{2}`.")]
    InvalidSamplerType(u32, String, String),
}

/// A prepared bind group returned as a result of [`AsBindGroup::as_bind_group`].
pub struct PreparedBindGroup {
    pub bindings: BindingResources,
    pub bind_group: BindGroup,
}

impl PreparedBindGroup {
    pub(crate) fn unprepare(&self) -> BindGroupBuilder {
        let mut data_buffer = vec![];
        BindGroupBuilder {
            binding_resources: UnpreparedBindingResources(
                self.bindings
                    .iter()

View on GitHub (pinned to 396ca72708)

Solutions

  1. Create the group via as_bind_group() as the message says, and use the returned PreparedBindGroup.
  2. If you own the implementation, only return this variant from paths you do not support and make sure your material plugin uses as_bind_group.
  3. Prefer the default Material preparation pipeline, which already handles this variant correctly.

Example fix

// before: unsupported creation path
let unprepared = material.unprepared_bind_group(layout_id, &layout, render_device)?; // Err(CreateBindGroupDirectly)

// after
let prepared = material.as_bind_group(layout_id, &layout, render_device, render_queue, &mut param)?;
let bind_group = prepared.bind_group;
Defensive patterns

Strategy: fallback

Try / catch

let group = match material.unprepared_bind_group(layout_id, &layout, render_device) {
    Ok(unprepared) => build_from(unprepared),
    Err(AsBindGroupError::CreateBindGroupDirectly) => {
        material.as_bind_group(layout_id, &layout, render_device, render_queue, &mut param)?.bind_group
    }
    Err(e) => return Err(e.into()),
};

Prevention

When it happens

Trigger: Calling unprepared_bind_group (or letting the MaterialBindGroupAllocator create the group from unprepared bindings) on a type whose implementation rejects that route and returns CreateBindGroupDirectly.

Common situations: Custom materials that take full control of bind group creation; bindless (descriptor-indexing) materials; migration between Bevy versions where material bind group creation paths changed; hand-rolled material preparation bypassing the default Material systems.

Related errors


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