{"record":{"id":"6c17c1e8350a7f5a","repo":"bevyengine/bevy","slug":"the-mesh-does-not-contain-any-joints-that-are-skin","errorCode":null,"errorMessage":"The mesh does not contain any joints that are skinned to vertices","messagePattern":"The mesh does not contain any joints that are skinned to vertices","errorType":"exception","errorClass":"SkinnedMeshBoundsError","httpStatus":null,"severity":"error","filePath":"crates/bevy_mesh/src/skinning.rs","lineNumber":109,"sourceCode":"    // Model-space AABBs that enclose the vertices skinned to a joint. Some\n    // joints may not be skinned to any vertices, so not every joint has an\n    // AABB.\n    //\n    // `aabb_index_to_joint_index` maps from an `aabbs` index to a joint index,\n    // which corresponds to `Mesh::ATTRIBUTE_JOINT_INDEX` and `SkinnedMesh::joints`.\n    //\n    // These arrays could be a single `Vec<(JointAabb, JointIndex)>`, but that\n    // would waste two bytes due to alignment.\n    //\n    // TODO: If https://github.com/bevyengine/bevy/issues/11570 is fixed, `Vec<_>`\n    // can be changed to `Box<[_]>`.\n    pub aabbs: Vec<JointAabb>,\n    pub aabb_index_to_joint_index: Vec<JointIndex>,\n}\n\n#[derive(Copy, Clone, PartialEq, Debug, Error)]\npub enum SkinnedMeshBoundsError {\n    #[error(\"The mesh does not contain any joints that are skinned to vertices\")]\n    NoSkinnedJoints,\n    #[error(transparent)]\n    MeshAttributeError(#[from] MeshAttributeError),\n}\n\nimpl SkinnedMeshBounds {\n    /// Create a `SkinnedMeshBounds` from a [`Mesh`].\n    ///\n    /// The mesh is expected to have position, joint index and joint weight\n    /// attributes. If any are missing then a [`MeshAttributeError`] is returned.\n    pub fn from_mesh(mesh: &Mesh) -> Result<SkinnedMeshBounds, SkinnedMeshBoundsError> {\n        let vertex_positions = expect_attribute_float32x3(mesh, Mesh::ATTRIBUTE_POSITION)?;\n        let vertex_influences = InfluenceIterator::new(mesh)?;\n\n        // Find the maximum joint index.\n        let Some(max_joint_index) = vertex_influences\n            .clone()\n            .map(|i| i.joint_index.0 as usize)","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/bevyengine/bevy/blob/396ca727080776bd313bb892423b7d94e03b81b4/crates/bevy_mesh/src/skinning.rs#L91-L127","documentation":"SkinnedMeshBoundsError::NoSkinnedJoints is returned by SkinnedMeshBounds::from_mesh, which computes per-joint AABBs so skinned meshes can be frustum-culled tightly. from_mesh iterates every vertex influence with a non-zero joint weight and adds that vertex's position to the influencing joint's AABB accumulator. The error means at least one weighted influence existed, yet no joint ever received a valid point: every accumulator finished empty. In practice the POSITION attribute does not cover the vertices that carry the weights (too short or empty), or all weighted positions are NaN (f32::min/max silently drop NaN, leaving empty AABBs).","triggerScenarios":"Calling SkinnedMeshBounds::from_mesh(&mesh) (or a system that derives skinned-mesh bounds) on a mesh whose JOINT_INDEX/JOINT_WEIGHT arrays contain entries with weight > 0, but where vertex_positions.get(vertex_index) is None for all of them: POSITION has fewer vertices than the joint arrays (or is empty), or every weighted vertex position is NaN. Note: all-zero weights or an empty vertex list returns Ok(default) instead, not this error.","commonSituations":"Hand-authored or procedurally generated skinned meshes with mismatched attribute lengths (skin data exported for more vertices than positions); glTF assets whose position stream is truncated; degenerate positions produced by bad bind-pose math; test meshes with placeholder joint weights but no real geometry.","solutions":["Make the POSITION attribute cover all skinned vertices: vertex count of POSITION must equal the count of JOINT_INDEX/JOINT_WEIGHT and be non-zero","Ensure at least one vertex with a non-zero weight has a finite (non-NaN) position; fix whatever produced NaN positions (bad inverse bind matrices, broken animation math)","Sanity-check the mesh before deriving bounds: compare attribute lengths and scan positions for NaN","If you cannot fix the asset, treat the error as a signal and fall back to mesh.compute_aabb() for culling"],"exampleFix":"// before: weights for 100 vertices but only 10 positions\nmesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions);          // 10 verts\nmesh.insert_attribute(Mesh::ATTRIBUTE_JOINT_INDEX, joint_indices);   // 100 verts\nmesh.insert_attribute(Mesh::ATTRIBUTE_JOINT_WEIGHT, joint_weights);  // 100 verts\nSkinnedMeshBounds::from_mesh(&mesh)?; // Err(NoSkinnedJoints)\n\n// after: keep all attribute arrays the same length\nassert_eq!(positions.len(), joint_indices.len());\nassert_eq!(positions.len(), joint_weights.len());\nmesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions);\nmesh.insert_attribute(Mesh::ATTRIBUTE_JOINT_INDEX, joint_indices);\nmesh.insert_attribute(Mesh::ATTRIBUTE_JOINT_WEIGHT, joint_weights);\nSkinnedMeshBounds::from_mesh(&mesh)?; // Ok(..)","handlingStrategy":"try-catch","validationCode":"// Verify skin data actually binds joints to existing vertices before deriving bounds\nfn skinning_is_sane(mesh: &Mesh) -> bool {\n    let Some(VertexAttributeValues::Float32x3(pos)) = mesh.attribute(Mesh::ATTRIBUTE_POSITION) else { return false };\n    let Some(VertexAttributeValues::Uint16x4(idx)) = mesh.attribute(Mesh::ATTRIBUTE_JOINT_INDEX) else { return false };\n    let Some(VertexAttributeValues::Float32x4(wgt)) = mesh.attribute(Mesh::ATTRIBUTE_JOINT_WEIGHT) else { return false };\n    let n = idx.len().min(wgt.len());\n    (0..n).any(|v| {\n        pos.get(v).is_some_and(|p| p.iter().all(|c| c.is_finite()))\n            && (0..4).any(|i| wgt[v][i] > 0.0)\n    })\n}","typeGuard":null,"tryCatchPattern":"match SkinnedMeshBounds::from_mesh(&mesh) {\n    Ok(bounds) => { /* insert for tight culling */ }\n    Err(SkinnedMeshBoundsError::NoSkinnedJoints) => {\n        warn!(\"mesh '{}' has weights but no bound joints; falling back to static AABB\", name);\n        let aabb = mesh.compute_aabb(); // fallback path\n    }\n    Err(SkinnedMeshBoundsError::MeshAttributeError(e)) => { /* fix attributes */ }\n}","preventionTips":["Keep POSITION, JOINT_INDEX and JOINT_WEIGHT arrays at identical, non-zero vertex counts","Reject or repair assets whose weighted vertices have NaN positions before runtime","Treat NoSkinnedJoints as an asset-quality error: log it and fall back to compute_aabb() rather than crashing"],"tags":["bevy","mesh","skinning","skinned-mesh","bounding-box","culling"],"backgroundTag":"skinned-mesh-no-bound-joints","analyzedSha":"396ca727080776bd313bb892423b7d94e03b81b4","analyzedAt":"2026-08-20T16:12:39.808Z","contentChangedAt":"2026-08-20T16:12:39.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}