bevyengine/bevy · error · GenerateTangentsError
missing indices
Error message
missing indices
What it means
GenerateTangentsError::MissingIndices (crates/bevy_mesh/src/mikktspace.rs:67), returned when Mesh::compute_tangents is called on a mesh without an index buffer. Mikktspace needs face connectivity (which vertices form each triangle) to compute per-triangle tangents; raw vertex soups carry no such information.
Source
Thrown at crates/bevy_mesh/src/mikktspace.rs:67
}
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)),
};View on GitHub (pinned to 396ca72708)
Solutions
- Generate an index buffer with mesh.merge_duplicate_vertices() (it welds the soup and produces Indices::U32)
- Or supply explicit triangle indices with mesh.insert_indices(Indices::U32(vec)) / Indices::U16
- Re-check ordering: compute tangents after indexing, not before
Example fix
// before: attribute-only mesh let mut mesh = Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::default()); mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions); mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals); mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs); mesh.compute_tangents(); // Err(MissingIndices) // after: weld into an indexed mesh first mesh.merge_duplicate_vertices().unwrap(); mesh.compute_tangents().unwrap();
Defensive patterns
Strategy: try-catch
Validate before calling
if mesh.indices().is_none() {
mesh.merge_duplicate_vertices()?; // produces Indices::U32
}
mesh.compute_tangents()?; Type guard
fn is_indexed(mesh: &Mesh) -> bool {
mesh.indices().is_some()
} Try / catch
match mesh.compute_tangents() {
Ok(()) => {}
Err(GenerateTangentsError::MissingIndices) => {
mesh.merge_duplicate_vertices().expect("weld failed");
mesh.compute_tangents()?; // retry once, now indexed
}
Err(e) => bevy::log::error!("tangent generation failed: {e}"),
} Prevention
- Index your meshes before compute_tangents — merge_duplicate_vertices is the cheapest route for soups
- Note duplicate_vertices() strips indices; re-index afterwards
- Order pipeline: attributes -> indices -> normals -> tangents
When it happens
Trigger: Calling compute_tangents() on a mesh built with only insert_attribute calls and no insert_indices / with_inserted_indices — e.g. a manually assembled triangle soup, or a mesh processed by duplicate_vertices() (which strips indices).
Common situations: Following the flat-normals recipe (duplicate_vertices -> compute_flat_normals) and then adding normal mapping; hand-written quad/triangle builders that rely on implicit ordering; importer paths that drop empty index buffers.
Related errors
- missing vertex attributes '{0}'
- cannot generate tangents for {0:?}
- the '{0}' vertex attribute should have {1:?} format
- mesh not suitable for tangent generation
- failed to generate tangents: {0}
AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20).
Data as JSON: /api/errors/e3576acaed060eeb.
Report an issue: GitHub.