bevyengine/bevy · error · MeshTrianglesError

Source mesh position data is not Float32x3

Error message

Source mesh position data is not Float32x3

What it means

Mesh::triangles() reads the POSITION attribute through VertexAttributeValues::as_float3() (crates/bevy_mesh/src/mesh.rs:2555). If the position data is stored in any variant other than Float32x3, as_float3 returns None and MeshTrianglesError::PositionsFormat is returned, because triangle extraction needs f32 xyz triples.

Source

Thrown at crates/bevy_mesh/src/index.rs:71

    #[error("Mesh winding inversion does not work for primitive topology `PointList`")]
    WrongTopology,

    /// This error occurs when you try to invert the winding for a mesh with
    /// * [`PrimitiveTopology::TriangleList`](super::PrimitiveTopology::TriangleList), but the indices are not in chunks of 3.
    /// * [`PrimitiveTopology::LineList`](super::PrimitiveTopology::LineList), but the indices are not in chunks of 2.
    #[error("Indices weren't in chunks according to topology")]
    AbruptIndicesEnd,
    #[error("Mesh access error: {0}")]
    MeshAccessError(#[from] MeshAccessError),
}

/// An error that occurred while trying to extract a collection of triangles from a [`Mesh`](super::Mesh).
#[derive(Debug, Error)]
pub enum MeshTrianglesError {
    #[error("Source mesh does not have primitive topology TriangleList or TriangleStrip")]
    WrongTopology,

    #[error("Source mesh position data is not Float32x3")]
    PositionsFormat,

    #[error("Face index data references vertices that do not exist")]
    BadIndices,
    #[error("mesh access error: {0}")]
    MeshAccessError(#[from] MeshAccessError),
}

/// An array of indices into the [`VertexAttributeValues`](super::VertexAttributeValues) for a mesh.
///
/// It describes the order in which the vertex attributes should be joined into faces.
#[derive(Debug, Clone, Reflect, PartialEq)]
#[reflect(Clone)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub enum Indices {
    U16(Vec<u16>),
    U32(Vec<u32>),
}

View on GitHub (pinned to 396ca72708)

Solutions

  1. Insert positions as Vec<Vec3> / Float32x3 so the attribute matches Mesh::ATTRIBUTE_POSITION.format
  2. Check mesh.attribute(Mesh::ATTRIBUTE_POSITION) and its variant before calling triangles()
  3. Re-insert the POSITION attribute as Float32x3 data before extraction

Example fix

// before
let tris: Vec<Triangle3d> = mesh.triangles()?.collect(); // Err(PositionsFormat)

// after
// ensure Float32x3 positions before extraction
let positions: Vec<[f32; 3]> = read_positions_as_f32();
mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions);
let tris: Vec<Triangle3d> = mesh.triangles()?.collect();
Defensive patterns

Strategy: validation

Validate before calling

fn positions_are_float32x3(mesh: &Mesh) -> bool {
    mesh.attribute(Mesh::ATTRIBUTE_POSITION)
        .is_some_and(|values| values.as_float3().is_some())
}

if positions_are_float32x3(&mesh) {
    let tris: Vec<_> = mesh.triangles()?.collect();
}

Type guard

fn float32x3_positions(mesh: &Mesh) -> Option<&[[f32; 3]]> {
    mesh.attribute(Mesh::ATTRIBUTE_POSITION)?.as_float3()
}

Try / catch

match mesh.triangles() {
    Ok(iter) => { /* use triangles */ }
    Err(MeshTrianglesError::PositionsFormat) => { /* re-insert POSITION as Float32x3 and retry */ }
    Err(other) => { /* topology / access errors */ }
}

Prevention

When it happens

Trigger: Calling mesh.triangles() on a mesh whose POSITION attribute is stored as a non-Float32x3 variant, typically from deserialized/reflect-built meshes or manually constructed attribute data.

Common situations: Meshes loaded from custom binary formats that store positions as doubles or packed formats; hot-reloaded or scene-serialized meshes where attribute formats drifted; test fixtures built with raw VertexAttributeValues.

Related errors


AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20). Data as JSON: /api/errors/bd00e57887301122. Report an issue: GitHub.