bevyengine/bevy · error · ShaderCacheError
Could not create shader module: {0}
Error message
Could not create shader module: {0} What it means
ShaderCacheError::CreateShaderModule(description) is produced in bevy_render's create_shader_module helper (pipeline_cache.rs:144-170): a wgpu error scope with ErrorFilter::Validation is pushed around module creation, and any validation error is captured and returned as this variant. It means naga/wgpu (or the driver) rejected the final WGSL at module creation. On native the error is caught early; on wasm the browser APIs are async, so the app instead crashes inside wgpu.
Source
Thrown at crates/bevy_shader/src/shader_cache.rs:546
let asset_id = self.module_path_to_asset_id.get(&module_path)?;
let shader = self.shaders.get(asset_id)?;
Some(shader.path.clone())
}
}
/// Type of error returned by a `PipelineCache` when the creation of a GPU pipeline object failed.
#[expect(missing_docs, reason = "Enum variants are self-explanatory")]
#[derive(Error, Debug)]
pub enum ShaderCacheError {
#[error(
"Pipeline could not be compiled because the following shader could not be loaded: {0:?}"
)]
ShaderNotLoaded(AssetId<Shader>),
#[error("Failed to process shader:\n{0}")]
ProcessShaderError(String),
#[error("Shader import not yet available.")]
ShaderImportNotYetAvailable,
#[error("Could not create shader module: {0}")]
CreateShaderModule(String),
}
#[cfg(test)]
mod tests {
use super::*;
fn test_cache() -> ShaderCache<String, ()> {
ShaderCache::new((), |_, source, _| match source {
ShaderCacheSource::Wgsl(wgsl) => Ok(wgsl),
_ => panic!("expected wgsl output"),
})
}
#[test]
fn import_resolution() {
let mut cache = test_cache();
View on GitHub (pinned to 227d3a6c66)
Solutions
- Read the wgpu validation description — it names the entry point and the rejected instruction
- Move implicit-LOD texture reads in vertex stages to textureSampleLevel/textureLoad
- Align @group/@binding annotations with your bind group layouts
- Validate the WGSL offline with the naga CLI or naga validators to get exact source spans
- Update wgpu/Bevy if validation rules changed
Example fix
// before — validation error: textureSample must only be used in fragment shaders
@vertex
fn vs_vs(vertex: VertexOutput) -> VertexOutput {
let light = textureSample(t_light_map, s_light_map, vertex.uv);
...
}
// after — explicit level is valid in vertex stage
@vertex
fn vs_vs(vertex: VertexOutput) -> VertexOutput {
let light = textureSampleLevel(t_light_map, s_light_map, vertex.uv, 0.0);
...
} Defensive patterns
Strategy: try-catch
Try / catch
match create_shader_module(device, descriptor, ValidateShader::Enabled) {
Ok(module) => module,
Err(ShaderCacheError::CreateShaderModule(desc)) => {
error!("wgpu rejected shader module: {desc}");
fallback_module
}
Err(e) => return Err(e),
} Prevention
- Validate WGSL offline with naga before shipping
- Keep bind group layouts and @binding annotations in one reviewed place to avoid drift
- Avoid implicit-LOD sampling in non-fragment stages
When it happens
Trigger: WGSL that passes preprocessing but fails naga/driver validation: using textureSample (implicit derivatives) in a @vertex entry point, unknown functions/types, bindings that do not match the bind group layouts, or feature-gated WGSL not supported by the adapter.
Common situations: Porting shaders from other APIs (HLSL/GLSL habits); @group/@binding indices drifting out of sync with layout changes; enabling experimental WGSL extensions on older drivers; validation rules tightening after a wgpu/Bevy upgrade.
Related errors
- RenderPipelineDescriptor has no FragmentState configured
- spirv not yet implemented
- Failed to process shader: {0}
- ParamSet parameter validation failed: {err}
- invalid glTF file: {0}
AI-assisted analysis of bevyengine/bevy@227d3a6c66 (2026-08-20).
Data as JSON: /api/errors/dbc0b39cfb08d8c6.
Report an issue: GitHub.