bevyengine/bevy · error · MeshMergeError
Incompatible vertex attribute types: {} and {}
Error message
Incompatible vertex attribute types: {} and {} What it means
MeshMergeError::IncompatibleVertexAttributes (crates/bevy_mesh/src/mesh.rs:3025), returned by Mesh::merge. When both meshes carry an attribute with the same id, merge extends self's buffer with other's; that only works when the VertexAttributeValues variants match exactly (Float32x3 with Float32x3, etc.). A type mismatch is rejected because concatenating different byte layouts would corrupt the buffer.
Source
Thrown at crates/bevy_mesh/src/mesh.rs:3025
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
)]
IncompatiblePrimitiveTopology {
self_primitive_topology: PrimitiveTopology,
other_primitive_topology: PrimitiveTopology,
},
#[error("Mesh access error: {0}")]
MeshAccessError(#[from] MeshAccessError),
}
#[cfg(test)]View on GitHub (pinned to 396ca72708)
Solutions
- Convert the mismatched attribute in one mesh so both use the same VertexAttributeValues variant, then merge again
- Remove the attribute you do not need from one mesh with mesh.remove_attribute(id) before merging
- Regenerate one mesh through the same builder/pipeline as the other so attribute formats line up by construction
Example fix
// before: same attribute id, different formats
let mut a = plane_mesh(); // POSITION: Float32x3
let mut b = imported_mesh(); // POSITION: Float32x2
a.merge(&b); // Err(IncompatibleVertexAttributes { self_attribute: POSITION(Float32x3), other: Float32x2 })
// after: convert b's positions to Float32x3 first
let fixed: Vec<[f32; 3]> = b.attribute(Mesh::ATTRIBUTE_POSITION).unwrap().as_float2().unwrap().iter().map(|p| [p[0], p[1], 0.0]).collect();
b.insert_attribute(Mesh::ATTRIBUTE_POSITION, fixed);
a.merge(&b).unwrap(); Defensive patterns
Strategy: try-catch
Validate before calling
use std::mem::discriminant;
use bevy_mesh::MeshVertexAttributeId;
// true when every attribute shared by both meshes uses the same values variant
fn attributes_compatible(a: &Mesh, b: &Mesh) -> bool {
a.attributes().all(|(attr, va)| {
b.attribute(attr.id).map_or(true, |vb| {
discriminant(va) == discriminant(vb)
})
})
} Try / catch
match a.merge(&b) {
Ok(()) => {}
Err(MeshMergeError::IncompatibleVertexAttributes { self_attribute, other_attribute }) => {
bevy::log::error!(
"cannot merge: {} vs {:?}", self_attribute.name, other_attribute.map(|a| a.name)
);
// convert or remove the offending attribute, then retry once
}
Err(e) => bevy::log::error!("merge failed: {e}"),
} Prevention
- Merged meshes should come from the same builder/pipeline so formats match by construction
- Compare VertexAttributeValues discriminants for shared attribute ids before merging
- Apply vertex compression only after all merges are done
When it happens
Trigger: Calling mesh_a.merge(&mesh_b) where an attribute with the same MeshVertexAttributeId exists in both but with different formats — e.g. ATTRIBUTE_POSITION as Float32x3 in one and Float32x2 in the other, or joint indices as Uint16x4 vs Uint32x4. Checked per attribute during the extend loop (mesh.rs:2182).
Common situations: Merging geometry from different sources (a Bevy primitive plus an imported glTF mesh); one side passed through compression/packing (Snorm8x4 normals) while the other kept plain Float32x3; custom attributes registered under the same id with inconsistent formats across code versions.
Related errors
- `Mesh::ATTRIBUTE_POSITION` vertex attributes should be of ty
- the '{0}' vertex attribute should have {1:?} format
- Incompatible primitive topologies: {:?} and {:?}
- missing vertex attributes '{0}'
- Mismatched vertex attribute values
AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20).
Data as JSON: /api/errors/73268ad964b9978f.
Report an issue: GitHub.