{"record":{"id":"26fd61a39108fc65","repo":"bevyengine/bevy","slug":"index-attribute-already-set","errorCode":null,"errorMessage":"Index attribute already set.","messagePattern":"Index attribute already set\\.","errorType":"exception","errorClass":"MeshMergeDuplicateVerticesError","httpStatus":null,"severity":"error","filePath":"crates/bevy_mesh/src/mesh.rs","lineNumber":3096,"sourceCode":"                .into_iter()\n                .map(|(id, data)| {\n                    (\n                        id,\n                        SerializedMeshAttributeData::from_mesh_attribute_data(data),\n                    )\n                })\n                .collect(),\n            indices: mesh.indices.replace(None).expect(MESH_EXTRACTED_ERROR),\n        }\n    }\n\n    /// Create a [`Mesh`] from a [`SerializedMesh`]. See the documentation for [`SerializedMesh`] for caveats.\n    ///\n    /// Use [`MeshDeserializer`] if you need to pass extra options to the deserialization process, such as specifying custom vertex attributes.\n    pub fn into_mesh(self) -> Mesh {\n        MeshDeserializer::default().deserialize(self)\n    }\n}\n\n/// Use to specify extra options when deserializing a [`SerializedMesh`] into a [`Mesh`].\n#[cfg(feature = \"serialize\")]\npub struct MeshDeserializer {\n    custom_vertex_attributes: HashMap<Box<str>, MeshVertexAttribute>,\n}\n\n#[cfg(feature = \"serialize\")]\nimpl Default for MeshDeserializer {\n    fn default() -> Self {\n        // Written like this so that the compiler can validate that we use all the built-in attributes.\n        // If you just added a new attribute and got a compile error, please add it to this list :)\n        const BUILTINS: [MeshVertexAttribute; Mesh::FIRST_AVAILABLE_CUSTOM_ATTRIBUTE as usize] = [\n            Mesh::ATTRIBUTE_POSITION,\n            Mesh::ATTRIBUTE_NORMAL,\n            Mesh::ATTRIBUTE_UV_0,\n            Mesh::ATTRIBUTE_UV_1,\n            Mesh::ATTRIBUTE_TANGENT,","sourceCodeStart":3078,"sourceCodeEnd":3114,"githubUrl":"https://github.com/bevyengine/bevy/blob/8d743eb7dc7fcff57a7d5744d0bbf89620b1b145/crates/bevy_mesh/src/mesh.rs#L3078-L3114","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["If you really want to rebuild indices, drop the existing buffer first: mesh.remove_indices(), then merge_duplicate_vertices()","For flat shading of indexed geometry use the documented recipe: mesh.duplicate_vertices() then mesh.compute_flat_normals()","For smooth shading keep the existing indices and call compute_smooth_normals() instead"],"exampleFix":"// before: Sphere builder already inserted indices\nlet mut mesh = Sphere::new(1.0).mesh().build();\nmesh.merge_duplicate_vertices(); // Err(IndicesAlreadySet)\n\n// after: drop old indices, then dedupe\nlet mut mesh = Sphere::new(1.0).mesh().build();\nmesh.remove_indices();\nmesh.merge_duplicate_vertices().unwrap();","handlingStrategy":"try-catch","validationCode":"if mesh.indices().is_none() {\n    mesh.merge_duplicate_vertices()?;\n} else {\n    // already indexed: nothing to dedupe, or remove_indices() first if rebuilding\n}","typeGuard":"fn is_unindexed(mesh: &Mesh) -> bool {\n    mesh.indices().is_none()\n}","tryCatchPattern":"match mesh.merge_duplicate_vertices() {\n    Ok(()) => {}\n    Err(MeshMergeDuplicateVerticesError::IndicesAlreadySet) => {\n        // decide: keep existing topology, or mesh.remove_indices() and retry\n    }\n    Err(MeshMergeDuplicateVerticesError::MeshAccessError(e)) => {\n        bevy::log::error!(\"mesh data unavailable: {e}\");\n    }\n}","preventionTips":["Remember merge_duplicate_vertices is for NON-indexed meshes only","Gate the call behind mesh.indices().is_none()","Keep the documented recipes straight: smooth normals need indices, flat normals need duplicate_vertices"],"tags":["bevy","mesh","indices","merge-duplicate-vertices","precondition"],"backgroundTag":"invalid-mesh-state","analyzedSha":"8d743eb7dc7fcff57a7d5744d0bbf89620b1b145","analyzedAt":"2026-08-20T16:12:39.808Z","contentChangedAt":"2026-08-20T16:12:39.808Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}