{"record":{"id":"f6f5ca113d378510","repo":"bevyengine/bevy","slug":"the-inner-and-outer-meshes-should-have-the-same-nu","errorCode":null,"errorMessage":"The inner and outer meshes should have the same number of vertices, and have required attributes","messagePattern":"The inner and outer meshes should have the same number of vertices, and have required attributes","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/bevy_mesh/src/primitives/dim2.rs","lineNumber":1453,"sourceCode":"            {\n                *b = 0;\n                *e = 0;\n                *f = points;\n            }\n\n            let mut positions = outer_positions;\n            positions.extend_from_slice(&inner_positions);\n\n            Mesh::new(\n                PrimitiveTopology::TriangleList,\n                RenderAssetUsages::default(),\n            )\n            .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)\n            .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)\n            .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)\n            .with_inserted_indices(Indices::U32(indices))\n        } else {\n            panic!(\"The inner and outer meshes should have the same number of vertices, and have required attributes\");\n        }\n    }\n}\n\nimpl<P> Extrudable for RingMeshBuilder<P>\nwhere\n    P: Primitive2d + Meshable,\n    P::Output: Extrudable,\n{\n    /// A list of the indices each representing a part of the perimeter of the mesh.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the following assumptions are not met.\n    ///\n    /// It is assumed that the inner and outer meshes have the same number of vertices.\n    /// If not, then the [`MeshBuilder`] of the underlying 2d primitive has generated\n    /// a different number of vertices for the inner and outer instances of the primitive.","sourceCodeStart":1435,"sourceCodeEnd":1471,"githubUrl":"https://github.com/bevyengine/bevy/blob/396ca727080776bd313bb892423b7d94e03b81b4/crates/bevy_mesh/src/primitives/dim2.rs#L1435-L1471","documentation":"Panic in RingMeshBuilder::build (crates/bevy_mesh/src/primitives/dim2.rs:1453). A Ring<P> is meshed by reading POSITION, NORMAL and UV_0 from both its inner and outer shape instances; the else-branch panics when get_vertex_attributes() returns None (an attribute is missing on either instance) or when the inner and outer vertex counts differ, because the ring's index stitching assumes matching loops.","triggerScenarios":"Calling .mesh().build() on a Ring<P> built around a custom Primitive2d whose Meshable implementation produces different vertex counts for the scaled inner instance vs the outer instance, or omits ATTRIBUTE_POSITION/ATTRIBUTE_NORMAL/ATTRIBUTE_UV_0. Built-in primitives satisfy the contract, so this is effectively a custom-builder contract violation.","commonSituations":"Implementing Meshable/Extrudable for your own 2D shape and wrapping it in Ring; a resolution parameter that affects vertex count non-uniformly when the shape is scaled; refactoring a custom builder and dropping an attribute insert.","solutions":["Make your custom builder emit the exact same number of vertices regardless of the instance's scale/parameters","Ensure the built mesh always inserts Mesh::ATTRIBUTE_POSITION, Mesh::ATTRIBUTE_NORMAL and Mesh::ATTRIBUTE_UV_0","Unit-test your Primitive2d inside Ring::new(...).mesh().build() so regressions panic in CI, not at runtime"],"exampleFix":"// before: vertex count depends on the shape's radius -> inner/outer mismatch\nimpl Meshable for MyShape {\n    fn build(&self) -> MeshBuilder {\n        let n = (self.radius * 32.0) as usize; // inner ring gets fewer vertices\n        MeshBuilder::new(&self.primitive, n)\n    }\n}\nRing::new(MyShape::default(), 2.0, 32).mesh().build(); // panic at dim2.rs:1453\n\n// after: fixed resolution independent of scale\nimpl Meshable for MyShape {\n    fn build(&self) -> MeshBuilder {\n        MeshBuilder::new(&self.primitive, 32)\n    }\n}","handlingStrategy":"validation","validationCode":"// For custom Primitive2d used inside Ring<P>: verify the contract before building\nfn ring_safe(primitive: &MyShape) -> bool {\n    let a = primitive.build().build();       // outer instance\n    let b = primitive.scaled(0.5).build().build(); // inner instance\n    let attr = |m: &Mesh| (m.attribute(Mesh::ATTRIBUTE_POSITION).is_some()\n        && m.attribute(Mesh::ATTRIBUTE_NORMAL).is_some()\n        && m.attribute(Mesh::ATTRIBUTE_UV_0).is_some());\n    attr(&a) && attr(&b)\n        && a.count_vertices() == b.count_vertices()\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Custom 2D builders used in Ring must emit a fixed vertex count independent of scale/parameters","Always insert POSITION, NORMAL and UV_0 in custom builder output","Test Ring::<YourShape>::new(..).mesh().build() in CI for custom primitives"],"tags":["bevy","mesh","primitives","ring","panic","custom-builder"],"backgroundTag":"custom-builder-contract-violation","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"}