bevyengine/bevy · error

Failed to get buffer

Error message

Failed to get buffer

What it means

IntoBinding for &StorageBuffer<T> calls self.binding().expect("Failed to get buffer"). binding() (storage_buffer.rs:82) returns None while the inner GPU Buffer has never been allocated — the buffer is only created inside write_buffer() on the first non-empty write. So converting the storage buffer into a BindingResource before the first write_buffer() call panics.

Source

Thrown at crates/bevy_render/src/render_resource/storage_buffer.rs:155

        if capacity < size || self.changed {
            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());
        }

        self.last_written_size = BufferSize::new(size);
    }
}

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

/// Stores data to be transferred to the GPU and made accessible to shaders as a dynamic storage buffer.
///
/// This is just a [`StorageBuffer`], but also allows you to set dynamic offsets.
///
/// Dynamic storage buffers can be made available to shaders in some combination of read/write mode, and can store large amounts
/// of data. Note however that WebGL2 does not support storage buffers, so consider alternative options in this case. Dynamic
/// storage buffers support multiple separate bindings at dynamic byte offsets and so have a
/// [`push`](DynamicStorageBuffer::push) method.
///
/// The contained data is stored in system RAM. [`write_buffer`](DynamicStorageBuffer::write_buffer)
/// queues copying of the data from system RAM to VRAM. The data within a storage buffer binding must conform to
/// [std430 alignment/padding requirements]. `DynamicStorageBuffer` takes care of serializing the inner type to conform to
/// these requirements. Each item [`push`](DynamicStorageBuffer::push)ed into this structure
/// will additionally be aligned to meet dynamic offset alignment requirements.
///

View on GitHub (pinned to 396ca72708)

Solutions

  1. Call storage_buffer.write_buffer(&queue) in a system ordered before the one that builds the bind group (use .before()/ordering or system sets)
  2. Make sure you set() data at least once — an empty scratch buffer has nothing to upload
  3. Instead of relying on IntoBinding, use binding() directly and skip bind-group creation while it returns None

Example fix

// before
fn build_bind_group(buffer: &StorageBuffer<MyData>, ...) {
    let entries = BindGroupEntries::sequential([&buffer]); // panics if never written
}

// after
fn write_buffer_system(buffer: &mut StorageBuffer<MyData>, queue: &RenderQueue) {
    buffer.write_buffer(queue); // allocates the GPU buffer first
}
fn build_bind_group_system(buffer: &StorageBuffer<MyData>, ...) {
    let entries = BindGroupEntries::sequential([&buffer]);
}
Defensive patterns

Strategy: validation

Validate before calling

// Before building the bind group:
if storage_buffer.buffer().is_none() {
    // write_buffer(&queue) has never run; skip this frame or write now
    return;
}

Prevention

When it happens

Trigger: Using &storage_buffer as a binding (e.g. BindGroupEntries::sequential_with_label(.., [&storage_buffer]) or entries.add(&storage_buffer)) in a system that runs before StorageBuffer::write_buffer(&queue) was ever called for that buffer.

Common situations: System-ordering mistakes where the bind-group-building system runs before the buffer-writing system each frame; forgetting to call write_buffer at all after set(); a newly added buffer that is bound on frame 0 before its first write.

Related errors


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