bevyengine/bevy · error · MeshMergeDuplicateVerticesError

Index attribute already set.

Error message

Index attribute already set.

What it means

MeshMergeDuplicateVerticesError::IndicesAlreadySet (crates/bevy_mesh/src/mesh.rs:3016), returned by Mesh::merge_duplicate_vertices when self.try_indices() finds an existing index buffer. The function's contract is to CREATE indices from a non-indexed vertex soup; an already-indexed mesh violates that precondition, so it refuses instead of overwriting valid topology.

Source

Thrown at crates/bevy_mesh/src/mesh.rs:3016

                        warn!(
                            "Deserialized mesh contains custom vertex attribute {attribute:?} that \
                            was not specified with `MeshDeserializer::add_custom_vertex_attribute`. Ignoring."
                        );
                        return None;
                    };
                    Some((id, data))
                })
                .collect()),
            indices: serialized_mesh.indices.into(),
            ..Mesh::new(serialized_mesh.primitive_topology, RenderAssetUsages::default())
        }
    }
}

/// Error that can occur when calling [`Mesh::merge_duplicate_vertices`]
#[derive(Error, Debug, Clone)]
pub enum MeshMergeDuplicateVerticesError {
    #[error("Index attribute already set.")]
    IndicesAlreadySet,
    #[error("Mesh access error: {0}")]
    MeshAccessError(#[from] MeshAccessError),
}

/// Error that can occur when calling [`Mesh::merge`].
#[derive(Error, Debug, Clone)]
pub enum MeshMergeError {
    #[error("Incompatible vertex attribute types: {} and {}", self_attribute.name, other_attribute.map(|a| a.name).unwrap_or("None"))]
    IncompatibleVertexAttributes {
        self_attribute: MeshVertexAttribute,
        other_attribute: Option<MeshVertexAttribute>,
    },
    #[error(
        "Incompatible primitive topologies: {:?} and {:?}",
        self_primitive_topology,
        other_primitive_topology
    )]

View on GitHub (pinned to 396ca72708)

Solutions

  1. If you really want to rebuild indices, drop the existing buffer first: mesh.remove_indices(), then merge_duplicate_vertices()
  2. For flat shading of indexed geometry use the documented recipe: mesh.duplicate_vertices() then mesh.compute_flat_normals()
  3. For smooth shading keep the existing indices and call compute_smooth_normals() instead

Example fix

// before: Sphere builder already inserted indices
let mut mesh = Sphere::new(1.0).mesh().build();
mesh.merge_duplicate_vertices(); // Err(IndicesAlreadySet)

// after: drop old indices, then dedupe
let mut mesh = Sphere::new(1.0).mesh().build();
mesh.remove_indices();
mesh.merge_duplicate_vertices().unwrap();
Defensive patterns

Strategy: try-catch

Validate before calling

if mesh.indices().is_none() {
    mesh.merge_duplicate_vertices()?;
} else {
    // already indexed: nothing to dedupe, or remove_indices() first if rebuilding
}

Type guard

fn is_unindexed(mesh: &Mesh) -> bool {
    mesh.indices().is_none()
}

Try / catch

match mesh.merge_duplicate_vertices() {
    Ok(()) => {}
    Err(MeshMergeDuplicateVerticesError::IndicesAlreadySet) => {
        // decide: keep existing topology, or mesh.remove_indices() and retry
    }
    Err(MeshMergeDuplicateVerticesError::MeshAccessError(e)) => {
        bevy::log::error!("mesh data unavailable: {e}");
    }
}

Prevention

When it happens

Trigger: Calling merge_duplicate_vertices() on any mesh that already has indices set: meshes from Bevy primitive builders (Sphere, Torus, ... all insert Indices::U32/U16), imported glTF geometry (indexed by nature), or a mesh where you previously called insert_indices/with_inserted_indices or an earlier successful merge_duplicate_vertices.

Common situations: Developer welds an imported indexed mesh to smooth seams and calls merge_duplicate_vertices directly; mixing up the documented normal-generation recipes (compute_smooth_normals wants indexed geometry, compute_flat_normals wants non-indexed) and reaching for the wrong helper.

Related errors


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