bevyengine/bevy · error · GenerateTangentsError

missing vertex attributes '{0}'

Error message

missing vertex attributes '{0}'

What it means

GenerateTangentsError::MissingVertexAttribute (crates/bevy_mesh/src/mikktspace.rs:69), returned by Mesh::compute_tangents when one of the three required attributes is absent. Tangent generation reads Mesh::ATTRIBUTE_POSITION, Mesh::ATTRIBUTE_NORMAL and Mesh::ATTRIBUTE_UV_0 (mikktspace.rs:88/97/106); the error carries the missing attribute's name so you know exactly which one to add.

Source

Thrown at crates/bevy_mesh/src/mikktspace.rs:69

    fn set_tangent(
        &mut self,
        tangent_space: Option<bevy_mikktspace::TangentSpace>,
        face: usize,
        vert: usize,
    ) {
        let idx = self.index(face, vert);
        self.tangents[idx] = tangent_space.unwrap_or_default().tangent_encoded();
    }
}

#[derive(Error, Debug)]
/// Failed to generate tangents for the mesh.
pub enum GenerateTangentsError {
    #[error("cannot generate tangents for {0:?}")]
    UnsupportedTopology(PrimitiveTopology),
    #[error("missing indices")]
    MissingIndices,
    #[error("missing vertex attributes '{0}'")]
    MissingVertexAttribute(&'static str),
    #[error("the '{0}' vertex attribute should have {1:?} format")]
    InvalidVertexAttributeFormat(&'static str, VertexFormat),
    #[error("mesh not suitable for tangent generation")]
    MikktspaceError(#[from] bevy_mikktspace::GenerateTangentSpaceError),
    #[error("Mesh access error: {0}")]
    MeshAccessError(#[from] MeshAccessError),
}

pub(crate) fn generate_tangents_for_mesh(
    mesh: &Mesh,
) -> Result<Vec<[f32; 4]>, GenerateTangentsError> {
    match mesh.primitive_topology() {
        PrimitiveTopology::TriangleList => {}
        other => return Err(GenerateTangentsError::UnsupportedTopology(other)),
    };

    let positions = mesh.try_attribute_option(Mesh::ATTRIBUTE_POSITION)?.ok_or(

View on GitHub (pinned to 396ca72708)

Solutions

  1. Compute normals first: mesh.compute_smooth_normals() (indexed) or duplicate_vertices()+compute_flat_normals()
  2. Author UVs: mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs) with one Vec2 per vertex
  3. Inspect the error payload — the '{0}' field names the exact missing attribute

Example fix

// before
mesh.compute_tangents(); // Err(MissingVertexAttribute("Vertex_Normal"))

// after: satisfy all three prerequisites
mesh.compute_smooth_normals().unwrap(); // adds ATTRIBUTE_NORMAL
mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs); // adds UV_0
mesh.compute_tangents().unwrap();
Defensive patterns

Strategy: validation

Validate before calling

fn has_tangent_inputs(mesh: &Mesh) -> bool {
    mesh.attribute(Mesh::ATTRIBUTE_POSITION).is_some()
        && mesh.attribute(Mesh::ATTRIBUTE_NORMAL).is_some()
        && mesh.attribute(Mesh::ATTRIBUTE_UV_0).is_some()
}

if has_tangent_inputs(&mesh) {
    mesh.compute_tangents()?;
}

Type guard

fn tangent_ready(mesh: &Mesh) -> bool {
    matches!(mesh.primitive_topology(), PrimitiveTopology::TriangleList)
        && mesh.indices().is_some()
        && mesh.attribute(Mesh::ATTRIBUTE_POSITION).is_some()
        && mesh.attribute(Mesh::ATTRIBUTE_NORMAL).is_some()
        && mesh.attribute(Mesh::ATTRIBUTE_UV_0).is_some()
}

Try / catch

if let Err(GenerateTangentsError::MissingVertexAttribute(name)) = mesh.compute_tangents() {
    bevy::log::error!("missing '{name}' before compute_tangents");
}

Prevention

When it happens

Trigger: Calling compute_tangents() on a mesh that has positions and indices but no ATTRIBUTE_NORMAL (normals not yet computed), or no ATTRIBUTE_UV_0 (UVs never authored or stripped by compression).

Common situations: Procedural meshes where the developer forgot normals before adding a normal map; imported assets whose UV set 0 was dropped; pipeline ordering bugs where tangents are computed before compute_smooth_normals has run.

Related errors


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