bevyengine/bevy · error · MeshAttributeError
Attribute "{0}" has unexpected format {1:?}
Error message
Attribute "{0}" has unexpected format {1:?} What it means
MeshAttributeError::UnexpectedFormat is returned when a mesh attribute exists but its VertexFormat differs from the one the API requires. The expect_attribute_* helpers in bevy_mesh::skinning accept exactly one variant: POSITION must be Float32x3, JOINT_INDEX must be Uint16x4, and JOINT_WEIGHT must be Float32x4. Any other variant for those attributes produces this error, with the offending format included in the message.
Source
Thrown at crates/bevy_mesh/src/skinning.rs:416
if joint_weight > 0.0 {
return Some(Influence {
vertex_index: self.vertex_index,
joint_index: JointIndex(joint_index),
joint_weight,
});
}
}
}
}
/// Generic error for when a mesh was expected to have a certain attribute with
/// a certain format.
#[derive(Copy, Clone, PartialEq, Debug, Error)]
pub enum MeshAttributeError {
#[error("Missing attribute \"{0}\"")]
MissingAttribute(&'static str),
#[error("Attribute \"{0}\" has unexpected format {1:?}")]
UnexpectedFormat(&'static str, VertexFormat),
}
// Implements a function that returns a mesh attribute's data or `MeshAttributeError`.
//
// ```
// impl_expect_attribute!(expect_attribute_float32x3, Float32x3, [f32; 3]);
//
// let positions: Vec<[f32; 3]> = expect_attribute_float32x3(mesh, Mesh::ATTRIBUTE_POSITION)?;
// ```
macro_rules! impl_expect_attribute {
($name:ident, $value_type:ident, $output_type:ty) => {
fn $name<'a>(
mesh: &'a Mesh,
attribute: MeshVertexAttribute,
) -> Result<&'a Vec<$output_type>, MeshAttributeError> {
match mesh.attribute(attribute) {
Some(VertexAttributeValues::$value_type(v)) => Ok(v),View on GitHub (pinned to 396ca72708)
Solutions
- Re-insert the attribute in the exact required format: convert JOINT_INDEX to Vec<[u16; 4]> and JOINT_WEIGHT to Vec<[f32; 4]> before inserting
- If joint indices exceed u16 range, remap/reindex joints so they fit u16, then store Uint16x4
- Read the error's format field to see the actual variant and write a targeted conversion
Example fix
// before: joint indices stored as u32
mesh.insert_attribute(Mesh::ATTRIBUTE_JOINT_INDEX, joint_indices_u32x4);
SkinnedMeshBounds::from_mesh(&mesh)?; // Err(UnexpectedFormat("JOINT_INDEX", Uint32x4))
// after: convert to the expected Uint16x4
let joint_indices_u16x4: Vec<[u16; 4]> = joint_indices_u32x4
.iter()
.map(|j| [j[0] as u16, j[1] as u16, j[2] as u16, j[3] as u16])
.collect();
mesh.insert_attribute(Mesh::ATTRIBUTE_JOINT_INDEX, joint_indices_u16x4);
SkinnedMeshBounds::from_mesh(&mesh)?; // Ok(..) Defensive patterns
Strategy: type-guard
Validate before calling
// Confirm the exact formats skinning requires before calling from_mesh
fn skin_attributes_well_formed(mesh: &Mesh) -> bool {
matches!(mesh.attribute(Mesh::ATTRIBUTE_POSITION), Some(VertexAttributeValues::Float32x3(_)))
&& matches!(mesh.attribute(Mesh::ATTRIBUTE_JOINT_INDEX), Some(VertexAttributeValues::Uint16x4(_)))
&& matches!(mesh.attribute(Mesh::ATTRIBUTE_JOINT_WEIGHT), Some(VertexAttributeValues::Float32x4(_)))
} Type guard
fn attribute_is_format(mesh: &Mesh, attr: MeshVertexAttribute, fmt: VertexFormat) -> bool {
mesh.attribute(attr).is_some_and(|v| VertexFormat::from(v) == fmt)
} Try / catch
match SkinnedMeshBounds::from_mesh(&mesh) {
Ok(bounds) => { /* use */ }
Err(SkinnedMeshBoundsError::MeshAttributeError(
MeshAttributeError::UnexpectedFormat(name, fmt),
)) => {
warn!("attribute {name} has format {fmt:?}; re-export the mesh with the standard skin formats");
}
Err(other) => warn!("skinning bounds failed: {other:?}"),
} Prevention
- Standardize on the canonical skin formats: POSITION Float32x3, JOINT_INDEX Uint16x4, JOINT_WEIGHT Float32x4
- Write importer checks that re-type u32 joint indices to u16 before insertion
- Add a debug_assert in asset loading that all three attributes match the expected variants
When it happens
Trigger: Calling SkinnedMeshBounds::from_mesh (or InfluenceIterator::new) on a mesh where, for example, JOINT_INDEX is stored as Uint32x4/Uint8x4 instead of Uint16x4, JOINT_WEIGHT is not Float32x4, or POSITION is not Float32x3 (e.g. half-float or Float32x2).
Common situations: Importers or packers that widen joint indices to u32 for precision; custom compressed vertex formats; hand-building a skinned mesh with the wrong Rust type for weights (e.g. [f32; 3] or u16 weights); meshes converted for vertex-compression pipelines.
Related errors
- Failed to insert attribute. Invalid attribute format for {}.
- Missing attribute "{0}"
- Mismatched vertex attribute values
- Source mesh position data is not Float32x3
- The mesh does not contain any joints that are skinned to ver
AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20).
Data as JSON: /api/errors/6a4d3ec43bfb3d02.
Report an issue: GitHub.