bevyengine/bevy · error · MeshMergeDuplicateVerticesError

Mesh access error: {0}

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.

Source

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

                            was not specified with `MeshDeserializer::add_custom_vertex_attribute`. Ignoring."
                        );
                        return None;
                    };
                    Some((id, data))
                })
                .collect()),
            indices: serialized_mesh.indices.into(),
            ..Mesh::new(serialized_mesh.primitive_topology, RenderAssetUsages::default())
        }
    }
}

/// Error that can occur when calling [`Mesh::merge_duplicate_vertices`]
#[derive(Error, Debug, Clone)]
pub enum MeshMergeDuplicateVerticesError {
    #[error("Index attribute already set.")]
    IndicesAlreadySet,
    #[error("Mesh access error: {0}")]
    MeshAccessError(#[from] MeshAccessError),
}

/// Error that can occur when calling [`Mesh::merge`].
#[derive(Error, Debug, Clone)]
pub enum MeshMergeError {
    #[error("Incompatible vertex attribute types: {} and {}", self_attribute.name, other_attribute.map(|a| a.name).unwrap_or("None"))]
    IncompatibleVertexAttributes {
        self_attribute: MeshVertexAttribute,
        other_attribute: Option<MeshVertexAttribute>,
    },
    #[error(
        "Incompatible primitive topologies: {:?} and {:?}",
        self_primitive_topology,
        other_primitive_topology
    )]
    IncompatiblePrimitiveTopology {
        self_primitive_topology: PrimitiveTopology,

View on GitHub (pinned to 396ca72708)

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@396ca72708 (2026-08-20). Data as JSON: /api/errors/f1a4b95744610f31. Report an issue: GitHub.