{"record":{"id":"56f98019016600d8","repo":"bevyengine/bevy","slug":"incompatible-primitive-topologies-and","errorCode":null,"errorMessage":"Incompatible primitive topologies: {:?} and {:?}","messagePattern":"Incompatible primitive topologies: (.+?) and (.+?)","errorType":"exception","errorClass":"MeshMergeError","httpStatus":null,"severity":"error","filePath":"crates/bevy_mesh/src/mesh.rs","lineNumber":3110,"sourceCode":"    /// 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,\n            Mesh::ATTRIBUTE_COLOR,\n            Mesh::ATTRIBUTE_JOINT_WEIGHT,\n            Mesh::ATTRIBUTE_JOINT_INDEX,\n        ];\n        Self {\n            custom_vertex_attributes: BUILTINS\n                .into_iter()\n                .map(|attribute| (attribute.name.into(), attribute))\n                .collect(),\n        }\n    }\n}\n\n#[cfg(feature = \"serialize\")]","sourceCodeStart":3092,"sourceCodeEnd":3128,"githubUrl":"https://github.com/bevyengine/bevy/blob/8d743eb7dc7fcff57a7d5744d0bbf89620b1b145/crates/bevy_mesh/src/mesh.rs#L3092-L3128","documentation":"MeshMergeError::IncompatiblePrimitiveTopology (crates/bevy_mesh/src/mesh.rs:3030), returned by Mesh::merge as its very first check (mesh.rs:2135). Merging concatenates index buffers with an offset, which is only meaningful when both meshes interpret those indices under the same primitive topology; mixing e.g. TriangleList with TriangleStrip would produce garbage geometry, so it is rejected up front.","triggerScenarios":"Calling mesh_a.merge(&mesh_b) where mesh_a was created with PrimitiveTopology::TriangleList and mesh_b with PrimitiveTopology::TriangleStrip (or any other differing variant such as LineList vs LineStrip).","commonSituations":"Merging a Bevy primitive (always TriangleList) with a custom mesh built as TriangleStrip to save indices; mixing 2D line geometry (LineList) with triangle meshes in a batching pass; topology defaults changing between asset importer versions.","solutions":["Rebuild one mesh with the same PrimitiveTopology you pass to Mesh::new for the other","Convert the strip mesh's triangle list (re-expand strip indices into triangles) before merging","Batch by topology: group meshes into merge sets that share a topology instead of merging across groups"],"exampleFix":"// before\nlet mut a = Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::default());\nlet b = Mesh::new(PrimitiveTopology::TriangleStrip, RenderAssetUsages::default());\na.merge(&b); // Err(IncompatiblePrimitiveTopology { self: TriangleList, other: TriangleStrip })\n\n// after: build both with the same topology\nlet b = Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::default());\na.merge(&b).unwrap();","handlingStrategy":"validation","validationCode":"fn same_topology(a: &Mesh, b: &Mesh) -> bool {\n    a.primitive_topology() == b.primitive_topology()\n}\n\nif same_topology(&mesh_a, &mesh_b) {\n    mesh_a.merge(&mesh_b)?;\n}","typeGuard":"fn mergeable_pair<'a>(a: &'a mut Mesh, b: &'a Mesh) -> Option<&'a mut Mesh> {\n    (a.primitive_topology() == b.primitive_topology()).then_some(a)\n}","tryCatchPattern":"if let Err(MeshMergeError::IncompatiblePrimitiveTopology {\n    self_primitive_topology,\n    other_primitive_topology,\n}) = mesh_a.merge(&mesh_b)\n{\n    bevy::log::error!(\"topology mismatch: {self_primitive_topology:?} vs {other_primitive_topology:?}\");\n}","preventionTips":["Set topology once in a shared factory for all meshes that will be batched together","Group merge candidates by PrimitiveTopology before merging","Beware TriangleStrip leftovers when porting older mesh code"],"tags":["bevy","mesh","merge","primitive-topology","mismatch"],"backgroundTag":"primitive-topology-mismatch","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"}