bevyengine/bevy · error · MeshMergeDuplicateVerticesError

Mesh access error

Error message

Mesh access error: {0}

What it means

MeshMergeDuplicateVerticesError::MeshAccessError (crates/bevy_mesh/src/mesh.rs:3018) wraps the MeshAccessError produced while merge_duplicate_vertices reads vertex data. In this code path the NotFound variant is tolerated (missing indices are expected), so the error that actually surfaces is MeshAccessError::ExtractedToRenderWorld: the mesh's RenderAssetUsages excluded MAIN_WORLD, so its vertex/index data was moved to the render world and is no longer readable from the app world.

Solutions

  1. Create the mesh with RenderAssetUsages::default() (MAIN_WORLD | RENDER_WORLD) so attributes stay readable
  2. Do all CPU-side processing (merge_duplicate_vertices, compute_smooth_normals) before spawning/adding the render-world-only mesh
  3. If the data is already extracted, re-create or re-import the mesh instead of trying to recover it

Example fix

// before: render-world-only mesh
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::RENDER_WORLD);
mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions);
// ...after the renderer extracts it:
mesh.merge_duplicate_vertices(); // Err(MeshAccessError::ExtractedToRenderWorld)

// after: keep data in the main world too
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::default());
mesh.merge_duplicate_vertices().unwrap();
Defensive patterns

Strategy: validation

Validate before calling

fn cpu_accessible(mesh: &Mesh) -> bool {
    mesh.asset_usage().contains(RenderAssetUsages::MAIN_WORLD)
}

if cpu_accessible(&mesh) {
    mesh.merge_duplicate_vertices()?;
}

Try / catch

if let Err(MeshMergeDuplicateVerticesError::MeshAccessError(
    MeshAccessError::ExtractedToRenderWorld,
)) = mesh.merge_duplicate_vertices()
{
    // re-create the mesh from source data with RenderAssetUsages::default()
}

Prevention

When it happens

Trigger: Creating a Mesh with RenderAssetUsages::RENDER_WORLD (main-world data dropped after extraction) and then calling merge_duplicate_vertices() on it after the asset has been prepared/extracted once.

Common situations: Memory-optimized asset pipelines that mark runtime-generated meshes as render-world-only; calling mesh post-processing (welding, normals) from a system that runs after rendering has already consumed the asset.

Related errors


AI-assisted analysis of bevyengine/bevy@8d743eb7dc (2026-08-20). Data as JSON: /api/errors/f1a4b95744610f31. Report an issue: GitHub.

Appendix: source

Thrown at crates/bevy_mesh/src/mesh.rs:3098

                    (
                        id,
                        SerializedMeshAttributeData::from_mesh_attribute_data(data),
                    )
                })
                .collect(),
            indices: mesh.indices.replace(None).expect(MESH_EXTRACTED_ERROR),
        }
    }

    /// Create a [`Mesh`] from a [`SerializedMesh`]. See the documentation for [`SerializedMesh`] for caveats.
    ///
    /// Use [`MeshDeserializer`] if you need to pass extra options to the deserialization process, such as specifying custom vertex attributes.
    pub fn into_mesh(self) -> Mesh {
        MeshDeserializer::default().deserialize(self)
    }
}

/// Use to specify extra options when deserializing a [`SerializedMesh`] into a [`Mesh`].
#[cfg(feature = "serialize")]
pub struct MeshDeserializer {
    custom_vertex_attributes: HashMap<Box<str>, MeshVertexAttribute>,
}

#[cfg(feature = "serialize")]
impl Default for MeshDeserializer {
    fn default() -> Self {
        // Written like this so that the compiler can validate that we use all the built-in attributes.
        // If you just added a new attribute and got a compile error, please add it to this list :)
        const BUILTINS: [MeshVertexAttribute; Mesh::FIRST_AVAILABLE_CUSTOM_ATTRIBUTE as usize] = [
            Mesh::ATTRIBUTE_POSITION,
            Mesh::ATTRIBUTE_NORMAL,
            Mesh::ATTRIBUTE_UV_0,
            Mesh::ATTRIBUTE_UV_1,
            Mesh::ATTRIBUTE_TANGENT,
            Mesh::ATTRIBUTE_COLOR,
            Mesh::ATTRIBUTE_JOINT_WEIGHT,

View on GitHub (pinned to 8d743eb7dc)