bevyengine/bevy · error

Failed to get buffer

Error message

Failed to get buffer

What it means

IntoBinding for &UniformBuffer<T> calls self.buffer().expect("Failed to get buffer"). buffer() returns Option<&Buffer> that is None until write_buffer() allocates the GPU buffer on the first write. Binding a UniformBuffer via IntoBinding before any write_buffer() call panics with this message.

Source

Thrown at crates/bevy_render/src/render_resource/uniform_buffer.rs:149

        if self.changed || self.buffer.is_none() {
            self.buffer = Some(device.create_buffer_with_data(&BufferInitDescriptor {
                label: make_buffer_label::<Self>(&self.label),
                usage: self.buffer_usage,
                contents: self.scratch.as_ref(),
            }));
            self.changed = false;
        } else if let Some(buffer) = &self.buffer {
            queue.write_buffer(buffer, 0, self.scratch.as_ref());
        }
    }
}

impl<'a, T: ShaderType + WriteInto> IntoBinding<'a> for &'a UniformBuffer<T> {
    #[inline]
    fn into_binding(self) -> BindingResource<'a> {
        self.buffer()
            .expect("Failed to get buffer")
            .as_entire_buffer_binding()
            .into_binding()
    }
}

/// Stores data to be transferred to the GPU and made accessible to shaders as a dynamic uniform buffer.
///
/// Dynamic uniform buffers are available to shaders on a read-only basis. Dynamic uniform buffers are commonly used to make
/// available to shaders runtime-sized arrays of parameters that are otherwise constant during shader execution, and are best
/// suited to data that is relatively small in size as they are only guaranteed to support up to 16kB per binding.
///
/// The contained data is stored in system RAM. [`write_buffer`](DynamicUniformBuffer::write_buffer) queues
/// copying of the data from system RAM to VRAM. Data in uniform buffers must follow [std140 alignment/padding requirements],
/// which is automatically enforced by this structure. Per the WGPU spec, uniform buffers cannot store runtime-sized array
/// (vectors), or structures with fields that are vectors.
///
/// Other options for storing GPU-accessible data are:
/// * [`BufferVec`](crate::render_resource::BufferVec)

View on GitHub (pinned to 396ca72708)

Solutions

  1. Call uniform_buffer.write_buffer(&queue) in a system that runs before bind-group construction
  2. Make sure the value is set (set()) so the first write actually uploads and allocates
  3. Check uniform_buffer.buffer().is_some() (or .binding().is_some()) before building the bind group and skip otherwise

Example fix

// before
fn make_bind_group(ub: &UniformBuffer Globals>, ...) {
    let entries = BindGroupEntries::sequential([&ub]); // panics
}

// after
fn upload_globals(ub: &mut UniformBuffer<Globals>, queue: &RenderQueue) {
    ub.write_buffer(queue); // first call allocates
}
fn make_bind_group(ub: &UniformBuffer<Globals>, ...) {
    let Some(binding) = ub.binding() else { return; };
    // build bind group with `binding`
}
Defensive patterns

Strategy: validation

Validate before calling

// Before IntoBinding / bind-group construction:
if uniform_buffer.buffer().is_none() {
    // value never written: call ub.write_buffer(&queue) first
    return;
}

Prevention

When it happens

Trigger: Using &uniform_buffer as BindingResource (BindGroupEntries / entries.add(&uniform_buffer)) before UniformBuffer::write_buffer(&queue) ran — e.g. the bind-group system runs before the uniform-writing system, or the value was never set.

Common situations: View/projection uniform buffers built in ExtractedView-style systems while the bind group is created in an unordered sibling system; first-frame access; new code path that sets the value conditionally so write_buffer is skipped.

Related errors


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