bevyengine/bevy · error
sampler attribute must have matching texture attribute
Error message
sampler attribute must have matching texture attribute
What it means
In `#[derive(AsBindGroup)]` (bevy_render's proc macro) every `#[sampler(n)]` binding must be paired with a matching `#[texture(m)]` attribute, conventionally directly above it on the same field, so the macro can derive the expected texture sample type and the fallback image. During macro expansion the code does `tex_attrs.as_ref().expect("sampler attribute must have matching texture attribute")`, so a sampler without a texture aborts compilation with this panic.
Source
Thrown at crates/bevy_render/macros/src/as_bind_group.rs:711
// Add the texture to the `BindlessResourceType` list in the
// bindless descriptor.
add_bindless_resource_type(
&render_path,
&mut bindless_resource_types,
binding_index,
bindless_resource_type,
);
}
BindingType::Sampler => {
let SamplerAttrs {
sampler_binding_type,
visibility,
..
} = get_sampler_attrs(nested_meta_items)?;
let TextureAttrs { dimension, .. } = tex_attrs
.as_ref()
.expect("sampler attribute must have matching texture attribute");
let visibility =
visibility.hygienic_quote("e! { #render_path::render_resource });
let fallback_image = get_fallback_image(&render_path, *dimension);
let expected_samplers = match sampler_binding_type {
SamplerBindingType::Filtering => {
quote!( [#render_path::render_resource::TextureSampleType::Float { filterable: true }] )
}
SamplerBindingType::NonFiltering => quote!([
#render_path::render_resource::TextureSampleType::Float { filterable: false },
#render_path::render_resource::TextureSampleType::Sint,
#render_path::render_resource::TextureSampleType::Uint,
]),
SamplerBindingType::Comparison => {
quote!( [#render_path::render_resource::TextureSampleType::Depth] )
}View on GitHub (pinned to 396ca72708)
Solutions
- Add a matching `#[texture(M, dimension = "2D")]` attribute directly above the `#[sampler(N)]` on the same field
- If you truly need no texture, remove the `#[sampler]` attribute and provide the binding manually (custom AsBindGroup impl or a storage binding)
- Verify binding indices stay unique per binding type after the fix
Example fix
// before
#[derive(Asset, AsBindGroup)]
struct MyMaterial {
#[uniform(0)]
color: Vec4,
#[sampler(1)]
tex: Option<Handle<Image>>,
}
// after
#[derive(Asset, AsBindGroup)]
struct MyMaterial {
#[uniform(0)]
color: Vec4,
#[texture(2, dimension = "2D")]
#[sampler(1)]
tex: Option<Handle<Image>>,
} Defensive patterns
Strategy: validation
Prevention
- Always write #[texture(M)] and #[sampler(N)] as a pair on the same field
- When deleting a texture field, delete both attributes together
- Copy working attribute blocks from bevy's material examples when adding new bindings
When it happens
Trigger: A field of an AsBindGroup struct annotated with only `#[sampler(N)]` (no `#[texture(M)]` on the same field), or a texture attribute that was deleted/moved during refactoring while the sampler attribute stayed behind.
Common situations: Writing a new custom material and forgetting the texture attribute; trimming a material down to a sampler-only field; copy-pasting binding attributes between fields and breaking the texture/sampler pairing.
Related errors
- Union types are not supported yet.
- Expected a Template type path
- Can only derive VariantDefaults for enums
- bevy_ecs should be found in manifest
- Failed to parse cargo manifest: {}
AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20).
Data as JSON: /api/errors/3303d074b6fad3f8.
Report an issue: GitHub.