bevyengine/bevy · critical

FullscreenMaterial::fragment_shader() must not return Shader

Error message

FullscreenMaterial::fragment_shader() must not return ShaderRef::Default

What it means

Bevy's FullscreenMaterial trait (used for fullscreen/post-processing passes) requires every implementation to supply a concrete fragment shader via `fn fragment_shader() -> ShaderRef`. `ShaderRef::Default` means 'no shader specified', so the pipeline builder in `FullscreenMaterial::key`/pipeline creation hits `unimplemented!()` and panics because it cannot construct a RenderPipelineDescriptor without shader source. This is a programmer error in the trait implementation, not a runtime condition.

Source

Thrown at crates/bevy_core_pipeline/src/fullscreen_material.rs:156

    render_device: Res<RenderDevice>,
    asset_server: Res<AssetServer>,
    fullscreen_shader: Res<FullscreenShader>,
) {
    let layout = BindGroupLayoutDescriptor::new(
        "fullscreen_material_bind_group_layout",
        &BindGroupLayoutEntries::sequential(
            ShaderStages::FRAGMENT,
            (
                texture_2d(TextureSampleType::Float { filterable: true }),
                sampler(SamplerBindingType::Filtering),
                uniform_buffer::<T>(true),
            ),
        ),
    );
    let sampler = render_device.create_sampler(&SamplerDescriptor::default());
    let shader = match T::fragment_shader() {
        ShaderRef::Default => {
            unimplemented!(
                "FullscreenMaterial::fragment_shader() must not return ShaderRef::Default"
            )
        }
        ShaderRef::Handle(handle) => handle,
        ShaderRef::Path(path) => asset_server.load(path),
    };

    let vertex_state = fullscreen_shader.to_vertex_state();
    let desc = RenderPipelineDescriptor {
        label: Some(format!("fullscreen_material_pipeline<{}>", type_name::<T>()).into()),
        layout: vec![layout.clone()],
        vertex: vertex_state,
        fragment: Some(FragmentState {
            shader,
            targets: vec![Some(ColorTargetState {
                format: TextureFormat::Rgba8UnormSrgb,
                blend: None,
                write_mask: ColorWrites::ALL,

View on GitHub (pinned to 78002f65fa)

Solutions

  1. Return a concrete shader from the impl: `fn fragment_shader() -> ShaderRef { ShaderRef::Path("shaders/my_post_process.wgsl".into()) }`
  2. If the shader is already loaded, return `ShaderRef::Handle(handle)` referencing a preloaded `Handle<Shader>`
  3. Verify the WGSL file exists at the given asset path so the pipeline does not fail on the next lookup
  4. Add a unit test asserting the impl never returns `ShaderRef::Default` (see exampleFix)

Example fix

// before
impl FullscreenMaterial for MyPostProcessMaterial {
    fn fragment_shader() -> ShaderRef {
        ShaderRef::Default // panics: unimplemented!()
    }
}

// after
impl FullscreenMaterial for MyPostProcessMaterial {
    fn fragment_shader() -> ShaderRef {
        ShaderRef::Path("shaders/my_post_process.wgsl".into())
    }
}

#[test]
fn fragment_shader_is_specified() {
    assert!(!matches!(MyPostProcessMaterial::fragment_shader(), ShaderRef::Default));
}
Defensive patterns

Strategy: type-guard

Validate before calling

#[test]
fn fullscreen_shader_is_specified() {
    use bevy::shader::ShaderRef;
    assert!(
        !matches!(MyPostProcessMaterial::fragment_shader(), ShaderRef::Default),
        "fragment_shader() must return Path or Handle, not Default"
    );
}

Type guard

fn is_shader_specified(shader_ref: &bevy::shader::ShaderRef) -> bool {
    !matches!(shader_ref, bevy::shader::ShaderRef::Default)
}

Prevention

When it happens

Trigger: Implementing a type that derives `AsBindGroup` and implements `FullscreenMaterial` (e.g. a custom post process effect in bevy_core_pipeline) while returning `ShaderRef::Default` from the associated function `fragment_shader()`. The panic fires when the render pipeline is initialized on the render thread.

Common situations: Copying the official post_processing example but deleting the `fragment_shader()` body; refactoring an enum-based shader selection and accidentally falling through to the `Default` variant; upgrading Bevy to a version where FullscreenMaterial moved from bevy_post_process to bevy_core_pipeline and the impl got stubbed out.

Related errors


AI-assisted analysis of bevyengine/bevy@78002f65fa (2026-08-16). Data as JSON: /api/errors/02c76b6c48c1507f. Report an issue: GitHub.