{"record":{"id":"4c33939eb015d684","repo":"bevyengine/bevy","slug":"mesh-vertex-attributes-must-be-required-but-g","errorCode":null,"errorMessage":"Mesh vertex attributes must be {required:?}, but got {provided:?}","messagePattern":"Mesh vertex attributes must be (.+?), but got (.+?)","errorType":"validation","errorClass":"MeshToMeshletMeshConversionError","httpStatus":null,"severity":"error","filePath":"crates/bevy_pbr/src/meshlet/from_mesh.rs","lineNumber":1095,"sourceCode":"        n.xy()\n    } else {\n        octahedral_wrap\n    }\n}\n\n// https://www.w3.org/TR/WGSL/#pack2x16snorm-builtin\nfn pack2x16snorm(v: Vec2) -> u32 {\n    let v = v.clamp(Vec2::NEG_ONE, Vec2::ONE);\n    let v = (v * 32767.0 + 0.5).floor().as_i16vec2();\n    bytemuck::cast(v)\n}\n\n/// An error produced by [`MeshletMesh::from_mesh`].\n#[derive(Error, Debug)]\npub enum MeshToMeshletMeshConversionError {\n    #[error(\"Mesh primitive topology is not TriangleList\")]\n    WrongMeshPrimitiveTopology,\n    #[error(\"Mesh vertex attributes must be {required:?}, but got {provided:?}\")]\n    WrongMeshVertexAttributes {\n        required: [MeshVertexAttribute; 3],\n        provided: Vec<MeshVertexAttribute>,\n    },\n    #[error(\"Mesh has no indices\")]\n    MeshMissingIndices,\n}\n","sourceCodeStart":1077,"sourceCodeEnd":1103,"githubUrl":"https://github.com/bevyengine/bevy/blob/396ca727080776bd313bb892423b7d94e03b81b4/crates/bevy_pbr/src/meshlet/from_mesh.rs#L1077-L1103","documentation":"`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.","triggerScenarios":"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.","commonSituations":"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.","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>)`"],"exampleFix":"// before: shape primitive has no UVs -> WrongMeshVertexAttributes\nlet mesh = Mesh::from(shape::Plane::from_size(2.0));\nlet meshlet = MeshletMesh::from_mesh(mesh, &viewer_position);\n\n// after: fill in the missing attribute, strip extras\nlet mut mesh = Mesh::from(shape::Plane::from_size(2.0));\nmesh.generate_padded_normals();\nmesh.generate_padded_uvs();\nmesh.remove_attribute(Mesh::ATTRIBUTE_TANGENT); // if present\nlet meshlet = MeshletMesh::from_mesh(mesh, &viewer_position);","handlingStrategy":"validation","validationCode":"fn has_meshlet_attributes(mesh: &Mesh) -> bool {\n    let required = [\n        (Mesh::ATTRIBUTE_POSITION.id, Mesh::ATTRIBUTE_POSITION.format),\n        (Mesh::ATTRIBUTE_NORMAL.id, Mesh::ATTRIBUTE_NORMAL.format),\n        (Mesh::ATTRIBUTE_UV_0.id, Mesh::ATTRIBUTE_UV_0.format),\n    ];\n    let provided: Vec<_> = mesh\n        .attributes()\n        .map(|(a, _)| (a.id, a.format))\n        .collect();\n    provided == required\n}\n\nif !has_meshlet_attributes(&mesh) {\n    mesh.generate_padded_normals();\n    mesh.generate_padded_uvs();\n    // also strip TANGENT / extra UVs, then re-check\n}","typeGuard":null,"tryCatchPattern":"match MeshletMesh::from_mesh(mesh, &viewer) {\n    Err(MeshToMeshletMeshConversionError::WrongMeshVertexAttributes { required, provided }) => {\n        warn!(?provided, ?required, \"mesh rejected by meshlet converter\");\n        continue;\n    }\n    Err(e) => return Err(e.into()),\n    Ok(meshlet) => { /* ... */ }\n}","preventionTips":["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"],"tags":["bevy","meshlet","mesh","vertex-attributes","asset-processing"],"backgroundTag":"mesh-vertex-attribute-validation","analyzedSha":"396ca727080776bd313bb892423b7d94e03b81b4","analyzedAt":"2026-08-20T16:12:39.808Z","contentChangedAt":"2026-08-20T16:12:39.808Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}