bevyengine/bevy · error · MeshToMeshletMeshConversionError
Mesh vertex attributes must be {required:?}, but got {provid
Error message
Mesh vertex attributes must be {required:?}, but got {provided:?} What it means
`MeshletMesh::from_mesh` converts a regular `Mesh` into the meshlet format used by bevy_pbr's meshlet renderer. Before conversion, `validate_input_mesh` (crates/bevy_pbr/src/meshlet/from_mesh.rs:249) requires the mesh's vertex attribute list to EXACTLY equal `[Mesh::ATTRIBUTE_POSITION (Float32x3), Mesh::ATTRIBUTE_NORMAL (Float32x3), Mesh::ATTRIBUTE_UV_0 (Float32x2)]` — same ids, same formats, same order, no extras. The error message prints both the required and the provided attribute lists so you can diff them.
Source
Thrown at crates/bevy_pbr/src/meshlet/from_mesh.rs:1095
n.xy()
} else {
octahedral_wrap
}
}
// https://www.w3.org/TR/WGSL/#pack2x16snorm-builtin
fn pack2x16snorm(v: Vec2) -> u32 {
let v = v.clamp(Vec2::NEG_ONE, Vec2::ONE);
let v = (v * 32767.0 + 0.5).floor().as_i16vec2();
bytemuck::cast(v)
}
/// An error produced by [`MeshletMesh::from_mesh`].
#[derive(Error, Debug)]
pub enum MeshToMeshletMeshConversionError {
#[error("Mesh primitive topology is not TriangleList")]
WrongMeshPrimitiveTopology,
#[error("Mesh vertex attributes must be {required:?}, but got {provided:?}")]
WrongMeshVertexAttributes {
required: [MeshVertexAttribute; 3],
provided: Vec<MeshVertexAttribute>,
},
#[error("Mesh has no indices")]
MeshMissingIndices,
}
View on GitHub (pinned to 396ca72708)
Solutions
- Diff the two lists in the error message against `[POSITION, NORMAL, UV_0]`; the `provided` list shows exactly what is missing, extra, or mis-ordered
- Generate missing data with the Mesh helpers: `mesh.generate_padded_normals()` and `mesh.generate_padded_uvs()`
- Remove extra attributes the meshlet pipeline rejects: `mesh.remove_attribute(Mesh::ATTRIBUTE_TANGENT)` (also VERTEX_COLOR, UV_1, …)
- Ensure formats are Float32x3/Float32x3/Float32x2; re-insert attributes in that exact order with `mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, Vec<Vec2>)`
Example fix
// before: shape primitive has no UVs -> WrongMeshVertexAttributes let mesh = Mesh::from(shape::Plane::from_size(2.0)); let meshlet = MeshletMesh::from_mesh(mesh, &viewer_position); // after: fill in the missing attribute, strip extras let mut mesh = Mesh::from(shape::Plane::from_size(2.0)); mesh.generate_padded_normals(); mesh.generate_padded_uvs(); mesh.remove_attribute(Mesh::ATTRIBUTE_TANGENT); // if present let meshlet = MeshletMesh::from_mesh(mesh, &viewer_position);
Defensive patterns
Strategy: validation
Validate before calling
fn has_meshlet_attributes(mesh: &Mesh) -> bool {
let required = [
(Mesh::ATTRIBUTE_POSITION.id, Mesh::ATTRIBUTE_POSITION.format),
(Mesh::ATTRIBUTE_NORMAL.id, Mesh::ATTRIBUTE_NORMAL.format),
(Mesh::ATTRIBUTE_UV_0.id, Mesh::ATTRIBUTE_UV_0.format),
];
let provided: Vec<_> = mesh
.attributes()
.map(|(a, _)| (a.id, a.format))
.collect();
provided == required
}
if !has_meshlet_attributes(&mesh) {
mesh.generate_padded_normals();
mesh.generate_padded_uvs();
// also strip TANGENT / extra UVs, then re-check
} Try / catch
match MeshletMesh::from_mesh(mesh, &viewer) {
Err(MeshToMeshletMeshConversionError::WrongMeshVertexAttributes { required, provided }) => {
warn!(?provided, ?required, "mesh rejected by meshlet converter");
continue;
}
Err(e) => return Err(e.into()),
Ok(meshlet) => { /* ... */ }
} Prevention
- Log mesh.attributes() for every asset during development so mismatches surface at import time, not at first render
- Run meshlet asset conversion as a build/processing step (AssetProcessor) where failures are per-asset and non-fatal
- Keep a project lint/helper that normalizes meshes (insert POSITION/NORMAL/UV_0, strip extras) before any from_mesh call
When it happens
Trigger: Calling `MeshletMesh::from_mesh(&mesh)` (or running the meshlet asset processor) on a mesh that is missing `NORMAL` or `UV_0`, carries extra attributes (`TANGENT`, `VERTEX_COLOR`, a second UV set), stores a required attribute in a different format (e.g. normals as non-Float32x3, UV as Float32x3), or lists attributes in a different order — the comparison uses iterator `.ne`, so any difference in the sequence fails.
Common situations: GLTF assets usually include TANGENT or extra UV channels; built-in shape primitives (`Mesh::from(shape::Plane::default())` etc.) ship without UVs; procedurally generated meshes often omit normals. Switching a project to `bevy_meshlet` assets surfaces all of these at asset-load time.
Related errors
- cannot convert VertexAttributeValues::{variant} to {into}
- Mesh has no indices
- The requested mesh data wasn't found in this mesh
- Color compression flag must be `COMPRESS_COLOR_FLOAT16` or `
- Malformed vertex attribute data
AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20).
Data as JSON: /api/errors/4c33939eb015d684.
Report an issue: GitHub.