bevyengine/bevy · critical
Mismatched vertex attribute values
Error message
Mismatched vertex attribute values
What it means
Defensive panic inside VertexAttributeValues::push_from, the vertex-copy helper used by Mesh::merge_duplicate_vertices (crates/bevy_mesh/src/mesh.rs:1465). It copies one vertex's attribute value from a source attribute store into a destination store, and this arm fires when the destination attribute is VertexAttributeValues::Float32 but the source value is a different variant. Within bevy both sides come from the same attribute map, so via public API this indicates corrupted mesh state or an engine bug; the user-facing cousin for two-mesh merges is MeshMergeError::IncompatibleVertexAttributes.
Source
Thrown at crates/bevy_mesh/src/vertex.rs:706
VertexAttributeValues::Float64(values) => bytes_of(&values[i]),
VertexAttributeValues::Float64x2(values) => bytes_of(&values[i]),
VertexAttributeValues::Float64x3(values) => bytes_of(&values[i]),
VertexAttributeValues::Float64x4(values) => bytes_of(&values[i]),
VertexAttributeValues::Unorm10_10_10_2(values) => bytes_of(&values[i]),
VertexAttributeValues::Unorm8x4Bgra(values) => bytes_of(&values[i]),
}
}
#[expect(
clippy::match_same_arms,
reason = "Although the `values` binding on some match arms may have matching types, each variant has different semantics; thus it's not guaranteed that they will use the same type forever."
)]
pub(crate) fn push_from(&mut self, source: &VertexAttributeValues, i: usize) {
match (self, source) {
(VertexAttributeValues::Float32(this), VertexAttributeValues::Float32(source)) => {
this.push(source[i]);
}
(VertexAttributeValues::Float32(_), _) => panic!("Mismatched vertex attribute values"),
(VertexAttributeValues::Sint32(this), VertexAttributeValues::Sint32(source)) => {
this.push(source[i]);
}
(VertexAttributeValues::Sint32(_), _) => panic!("Mismatched vertex attribute values"),
(VertexAttributeValues::Uint32(this), VertexAttributeValues::Uint32(source)) => {
this.push(source[i]);
}
(VertexAttributeValues::Uint32(_), _) => panic!("Mismatched vertex attribute values"),
(VertexAttributeValues::Float32x2(this), VertexAttributeValues::Float32x2(source)) => {
this.push(source[i]);
}
(VertexAttributeValues::Float32x2(_), _) => {
panic!("Mismatched vertex attribute values")
}
(VertexAttributeValues::Sint32x2(this), VertexAttributeValues::Sint32x2(source)) => {
this.push(source[i]);
}
(VertexAttributeValues::Sint32x2(_), _) => panic!("Mismatched vertex attribute values"),View on GitHub (pinned to 396ca72708)
Solutions
- Treat this as an engine bug or corrupted mesh state: minimize the mesh that triggers it and report it to the bevy issue tracker with a repro
- Audit any custom code that constructs or mutates Mesh attribute maps directly and ensure each attribute id always maps to one VertexAttributeValues variant
- If you were merging two meshes with different formats (e.g. Float32 vs Float32x3 for the same attribute name), normalize both to the same format first and use Mesh::merge, which reports incompatibilities as a Result instead of panicking
Defensive patterns
Strategy: validation
Validate before calling
// Defensive check before deduplicating or merging: one variant per attribute id
fn attribute_variants_consistent(mesh: &Mesh) -> bool {
// Well-formed meshes always satisfy this; useful after custom manipulation
mesh.attributes().all(|(_, data)| {
VertexFormat::from(&data.values) == VertexFormat::from(&data.values)
})
}
// Practical guard for merging two meshes (the user-facing cousin)
fn mergeable(a: &Mesh, b: &Mesh) -> bool {
a.primitive_topology() == b.primitive_topology()
&& a.attributes().all(|(id, val)| {
b.attribute_by_id(*id)
.map_or(true, |other| std::mem::discriminant(val) == std::mem::discriminant(other))
})
} Type guard
fn attribute_formats_match(a: &VertexAttributeValues, b: &VertexAttributeValues) -> bool {
std::mem::discriminant(a) == std::mem::discriminant(b)
} Prevention
- Use Mesh::merge's Result API and handle MeshMergeError::IncompatibleVertexAttributes instead of relying on panics
- Keep one VertexFormat per attribute id across all meshes produced by your pipeline
- If you hit this panic on unmodified meshes, capture a minimal repro and report it to bevy
When it happens
Trigger: Mesh::merge_duplicate_vertices on a mesh whose attribute state is internally inconsistent, such that the same attribute id resolves to a Float32 store on one side and a non-Float32 store on the other. Not reachable by merging two ordinary meshes (that path returns a Result error instead).
Common situations: Almost always an engine-level invariant violation: a bug in custom mesh-manipulation code or a corrupted/deserialized Mesh. Practically unreachable for well-formed meshes, since push_from is pub(crate) and both operands share one attribute map.
Related errors
- Failed to insert attribute. Invalid attribute format for {}.
- Attribute "{0}" has unexpected format {1:?}
- Source mesh position data is not Float32x3
- The number of vertices exceeds u32::MAX
- `Mesh::ATTRIBUTE_POSITION` vertex attributes should be of ty
AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20).
Data as JSON: /api/errors/80fd761534ed5904.
Report an issue: GitHub.