bevyengine/bevy · error · AccessFailed

Malformed vertex attribute data

Error message

Malformed vertex attribute data

What it means

AccessFailed::MalformedData is produced in BufferAccessor::iter (crates/bevy_gltf/src/vertex_attributes.rs:51-56) when gltf::accessor::Iter::new returns None: the accessor cannot be decoded against the loaded buffers. Per the doc comment this covers stride mismatches and buffer-view slicing failures — e.g. accessor byteOffset/byteLength exceeding its bufferView, a view out of bounds of its buffer, or a stride invalid for the data type. It surfaces to callers as ConvertAttributeError::AccessFailed(MalformedData, accessor_index) ('Malformed vertex attribute data in accessor N').

Source

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

        self,
        value: T,
        normalized_ctor: impl Fn(T) -> U,
        unnormalized_ctor: impl Fn(T) -> U,
    ) -> U {
        if self.0 {
            normalized_ctor(value)
        } else {
            unnormalized_ctor(value)
        }
    }
}

/// An error that occurs when accessing buffer data
#[derive(Error, Debug)]
pub enum AccessFailed {
    /// Accessing the data failed because of an issue like a mismatch in stride,
    /// or a buffer view slice failing.
    #[error("Malformed vertex attribute data")]
    MalformedData,
    /// The format supplied is unsupported for this operation.
    #[error("Unsupported vertex attribute format")]
    UnsupportedFormat,
}

/// Helper for reading buffer data
struct BufferAccessor<'a> {
    accessor: gltf::Accessor<'a>,
    buffer_data: &'a Vec<Vec<u8>>,
    normalization: Normalization,
}

impl<'a> BufferAccessor<'a> {
    /// Creates an iterator over the elements in this accessor
    fn iter<T: gltf::accessor::Item>(self) -> Result<gltf::accessor::Iter<'a, T>, AccessFailed> {
        gltf::accessor::Iter::new(self.accessor, |buffer: gltf::Buffer| {
            self.buffer_data.get(buffer.index()).map(Vec::as_slice)

View on GitHub (pinned to 396ca72708)

Solutions

  1. Run gltf-validator — it reports out-of-bounds accessors/bufferViews precisely.
  2. Re-export the model from the source DCC.
  3. If you generate glTF yourself, recompute byteOffset/byteLength/stride so every accessor fits inside its bufferView and every view inside its buffer.
Defensive patterns

Strategy: validation

Validate before calling

fn accessors_fit(doc: &gltf::Document, buffers: &[Vec<u8>]) -> bool {
    doc.accessors().all(|a| {
        a.view().map(|v| {
            let buf = buffers.get(v.buffer().index()).map(|b| b.len()).unwrap_or(0);
            let end = v.offset() + v.length();
            let acc_end = a.offset().unwrap_or(0) + a.size();
            end <= buf && acc_end <= v.length()
        }).unwrap_or(false)
    })
}

Try / catch

match err {
    ConvertAttributeError::AccessFailed(AccessFailed::MalformedData, idx) => {
        error!("accessor {idx} does not decode (offset/length/stride vs buffer); re-export or validate the file");
    }
    other => return Err(other.into()),
}

Prevention

When it happens

Trigger: Any mesh attribute accessor whose declared offsets/lengths/strides disagree with the actual buffer bytes — e.g. after manual JSON edits, corrupt downloads, or exporters writing byteLength wrong; sparse/odd strides not representable by the gltf iterator.

Common situations: Hand-tuned buffer optimizations, files truncated then 'repaired', quantization pipelines writing invalid accessor metadata.

Understand the failure class

Related errors


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