bevyengine/bevy · error
`Mesh::ATTRIBUTE_POSITION` vertex attributes should be of ty
Error message
`Mesh::ATTRIBUTE_POSITION` vertex attributes should be of type `float3`
What it means
Raised by Mesh::compute_flat_normals (crates/bevy_mesh/src/mesh.rs:1683). The function reads Mesh::ATTRIBUTE_POSITION through VertexAttributeValues::as_float3(), which returns Some only for the Float32x3 variant; any other storage format triggers this expect panic. Flat normals are computed per triangle, so positions must be exact 3-component floats.
Source
Thrown at crates/bevy_mesh/src/mesh.rs:1683
/// attributes.
///
/// FIXME: This should handle more cases since this is called as a part of gltf
/// mesh loading where we can't really blame users for loading meshes that might
/// not conform to the limitations here!
pub fn try_compute_flat_normals(&mut self) -> Result<(), MeshAccessError> {
assert!(
self.try_indices_option()?.is_none(),
"`compute_flat_normals` can't work on indexed geometry. Consider calling either `Mesh::compute_smooth_normals` or `Mesh::duplicate_vertices` followed by `Mesh::compute_flat_normals`."
);
assert!(
matches!(self.primitive_topology, PrimitiveTopology::TriangleList),
"`compute_flat_normals` can only work on `TriangleList`s"
);
let positions = self
.try_attribute(Mesh::ATTRIBUTE_POSITION)?
.as_float3()
.expect("`Mesh::ATTRIBUTE_POSITION` vertex attributes should be of type `float3`");
let normals: Vec<_> = positions
.as_chunks()
.0
.iter()
.flat_map(|&[a, b, c]| [triangle_normal(a, b, c); 3])
.collect();
self.try_insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
}
/// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared
/// vertices.
///
/// This method weights normals by the angles of the corners of connected triangles, thus
/// eliminating triangle area and count as factors in the final normal. This does make it
/// somewhat slower than [`Mesh::compute_area_weighted_normals`] which does not need to
/// greedily normalize each triangle's normal or calculate corner angles.View on GitHub (pinned to 396ca72708)
Solutions
- Insert positions as Vec<[f32; 3]> / Float32x3 from the start
- Rewrite the existing attribute in place: take the old values, convert each to [f32; 3], and re-insert with mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, converted)
- Build geometry through Bevy's MeshBuilder/primitive helpers, which always emit Float32x3 positions
Example fix
// before: positions stored as 2-component floats let mut mesh = Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::default()); mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, vec![[0.0f32, 0.0]; 30]); mesh.compute_flat_normals(); // panic: expects float3 // after: store positions as Float32x3 let mut mesh = Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::default()); mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, vec![[0.0f32, 0.0, 0.0]; 30]); mesh.compute_flat_normals();
Defensive patterns
Strategy: validation
Validate before calling
fn positions_are_float3(mesh: &Mesh) -> bool {
mesh.attribute(Mesh::ATTRIBUTE_POSITION)
.is_some_and(|v| v.as_float3().is_some())
}
if positions_are_float3(&mesh) {
mesh.compute_flat_normals()?;
} Type guard
fn float3_position_mesh(mesh: &Mesh) -> Option<&Vec<[f32; 3]>> {
mesh.attribute(Mesh::ATTRIBUTE_POSITION)?.as_float3()
} Prevention
- Always insert ATTRIBUTE_POSITION from Vec<[f32; 3]> data
- Validate attribute formats once at asset ingestion, not at every compute_* call
- Prefer Bevy primitive/MeshBuilder helpers that emit correct formats by construction
When it happens
Trigger: Calling compute_flat_normals() on a non-indexed TriangleList mesh whose ATTRIBUTE_POSITION was inserted with a format other than Float32x3 (e.g. Vec<[f32; 2]> -> Float32x2, Float32x4, or a packed integer format). The indexed/topology asserts earlier in the function fire first, so reaching this panic also implies those checks passed.
Common situations: Hand-built meshes where positions come from 2D data (heightmaps, Vec2 outlines) or from a pipeline that stores positions as vec4 (w=1) for math convenience; converting a raw GPU buffer with an unexpected stride into a Bevy Mesh.
Related errors
- Incompatible vertex attribute types: {} and {}
- the '{0}' vertex attribute should have {1:?} format
- Failed to insert attribute. Invalid attribute format for {}.
- The number of vertices exceeds u32::MAX
- missing vertex attributes '{0}'
AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20).
Data as JSON: /api/errors/edd10533d6745b9a.
Report an issue: GitHub.