bevyengine/bevy · error · ConvertAttributeError

Vertex attribute {0} has format {1:?} but expected {3:?} for

Error message

Vertex attribute {0} has format {1:?} but expected {3:?} for target attribute {2}

What it means

ConvertAttributeError::WrongFormat(String semantic, VertexFormat loaded, String attr, VertexFormat expected) is thrown at vertex_attributes.rs:333-345 after conversion: the materialized values' VertexFormat must exactly equal the fixed format of the target MeshVertexAttribute (POSITION must be Float32x3, UV_0 Float32x2, JOINT_INDEX Uint16x4, ...). The classic trigger is an accessor whose format has no conversion path for its semantic — e.g. TEXCOORD_0 as UNnormalized U8x2: into_tex_coord_values (line 241-251) only converts normalized U8x2/U16x2, so values stay Uint8x2 while ATTRIBUTE_UV_0 requires Float32x2.

Source

Thrown at crates/bevy_gltf/src/vertex_attributes.rs:267

            s => s.into_any_values(false),
        }
    }
}

enum ConversionMode {
    Any,
    Rgba,
    JointIndex,
    JointWeight,
    TexCoord,
}

/// Errors that can occur during the `convert_attribute` function.
#[derive(Error, Debug)]
pub enum ConvertAttributeError {
    /// The loaded format ws different than the attribute's intended format.
    /// Such as if a `Float32x3` was loaded as a `Float32x2`.
    #[error("Vertex attribute {0} has format {1:?} but expected {3:?} for target attribute {2}")]
    WrongFormat(String, VertexFormat, String, VertexFormat),
    /// Fetching values from the glTF Accessor failed
    #[error("{0} in accessor {1}")]
    AccessFailed(AccessFailed, usize),
    /// A vertex attribute name was not one of the gltf crate's well-known attributes,
    /// nor was it registered by a user as a custom attribute. Therefore it is unknown.
    #[error("Unknown vertex attribute {0}")]
    UnknownName(String),
}

/// map glTF vertex attributes into their `MeshVertexAttribute` forms, optionally
/// converting values if necessary.
pub fn convert_attribute(
    semantic: gltf::Semantic,
    accessor: gltf::Accessor,
    buffer_data: &Vec<Vec<u8>>,
    custom_vertex_attributes: &HashMap<Box<str>, MeshVertexAttribute>,
    convert_coordinates: bool,

View on GitHub (pinned to 396ca72708)

Solutions

  1. Re-export attributes in the formats Bevy converts for that semantic (float32 positions/uvs; normalized U8/U16 texcoords; unnormalized U8/U16 joints; normalized weights).
  2. For _CUSTOM_ attributes, register the MeshVertexAttribute with a format matching the accessor data (or convert the data to match the registered format).
  3. Check the error fields: arg 1 is the semantic, arg 2 what was loaded, arg 4 what was expected — adjust whichever side is wrong.

Example fix

// TEXCOORD_0 accessor (before)
{ "componentType": 5121, "type": "VEC2", "normalized": false }
// (after) normalized bytes convert to Float32x2 as expected
{ "componentType": 5121, "type": "VEC2", "normalized": true }
Defensive patterns

Strategy: validation

Validate before calling

use bevy_mesh::VertexFormat;
fn semantic_format_ok(semantic: &gltf::Semantic, loaded: VertexFormat) -> bool {
    let expected = match semantic {
        gltf::Semantic::Positions => VertexFormat::Float32x3,
        gltf::Semantic::Normals => VertexFormat::Float32x3,
        gltf::Semantic::Tangents => VertexFormat::Float32x4,
        gltf::Semantic::TexCoords(0..=1) => VertexFormat::Float32x2,
        gltf::Semantic::Colors(0) => VertexFormat::Float32x4,
        gltf::Semantic::Joints(0) => VertexFormat::Uint16x4,
        gltf::Semantic::Weights(0) => VertexFormat::Float32x4,
        _ => return true,
    };
    loaded == expected
}

Type guard

fn is_wrong_format(err: &ConvertAttributeError) -> Option<(&str, &VertexFormat, &VertexFormat)> {
    match err {
        ConvertAttributeError::WrongFormat(sem, loaded, _, expected) => Some((sem, loaded, expected)),
        _ => None,
    }
}

Try / catch

match err {
    ConvertAttributeError::WrongFormat(sem, loaded, name, expected) => {
        error!("{sem} stored as {loaded:?} but {name} needs {expected:?}; re-export the attribute in a convertible format");
    }
    other => return Err(other.into()),
}

Prevention

When it happens

Trigger: TEXCOORD_0 with "normalized": false byte/short data; JOINTS_0 as normalized U16x4 (loads Unorm16x4, expected Uint16x4); custom vertex attributes registered with a MeshVertexAttribute format that does not match the stored data; POSITION exported as VEC2.

Common situations: Quantized/optimized glTFs (KHR_mesh_quantization derivatives) using unnormalized bytes; teams registering custom attributes with a guessed format; exporters writing unusual joint encodings.

Related errors


AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20). Data as JSON: /api/errors/4ed5ac30ef7541fc. Report an issue: GitHub.