bevyengine/bevy · error · WriteBufferRangeError

BufferNotInitialized

BufferNotInitialized

Error message

the gpu buffer is not initialized

What it means

WriteBufferRangeError::BufferNotInitialized is returned when write_buffer_range is called while the underlying wgpu buffer is None (buffer_vec.rs:212-213): the GPU buffer was never created because neither write_buffer() nor reserve() has run yet. Partial-range writes can only be queued onto an existing buffer.

Source

Thrown at crates/bevy_render/src/render_resource/buffer_vec.rs:982

    T: NoUninit + Default,
{
    /// Pushes `count` copies of `T::default` to the array.
    pub fn push_multiple_init(&mut self, count: usize) -> usize {
        debug_assert_eq!(self.uninit_element_count, 0);
        let index = self.values.len();
        self.values.extend(iter::repeat_n(T::default(), count));
        index
    }
}

/// Error returned when `write_buffer_range` fails
///
/// See [`RawBufferVec::write_buffer_range`] [`BufferVec::write_buffer_range`]
#[derive(Debug, Eq, PartialEq, Copy, Clone, Error)]
pub enum WriteBufferRangeError {
    #[error("the range is bigger than the capacity of the buffer")]
    RangeBiggerThanBuffer,
    #[error("the gpu buffer is not initialized")]
    BufferNotInitialized,
    #[error("there are no values to upload")]
    NoValuesToUpload,
}

#[inline]
#[cfg_attr(
    not(feature = "type_label_buffers"),
    expect(
        clippy::extra_unused_type_parameters,
        reason = "conditional compilation"
    )
)]
pub(crate) fn make_buffer_label<'a, T>(label: &'a Option<String>) -> Option<&'a str> {
    #[cfg(feature = "type_label_buffers")]
    if label.is_none() {
        return Some(core::any::type_name::<T>());
    }

View on GitHub (pinned to 396ca72708)

Solutions

  1. Initialize first: call vec.write_buffer(render_device, render_queue) once (creates and fills the buffer), or reserve(n, render_device).
  2. Order systems so the buffer-creating write happens before any partial-range updates (add explicit system ordering).
  3. Match on the error and initialize on demand before retrying.

Example fix

// before
let mut vec = RawBufferVec::default();
vec.push(value);
vec.write_buffer_range(queue, 0..1)?; // Err: buffer is None

// after
if vec.buffer().is_none() {
    vec.write_buffer(render_device, queue); // creates the GPU buffer
}
vec.write_buffer_range(queue, 0..1)?;
Defensive patterns

Strategy: validation

Validate before calling

if vec.buffer().is_none() {
    vec.write_buffer(render_device, render_queue); // create the GPU buffer first
}
vec.write_buffer_range(queue, 0..vec.len())?;

Try / catch

match vec.write_buffer_range(queue, 0..vec.len()) {
    Err(WriteBufferRangeError::BufferNotInitialized) => {
        vec.write_buffer(render_device, render_queue); // init, then retry
        vec.write_buffer_range(queue, 0..vec.len())?
    }
    other => other?,
}

Prevention

When it happens

Trigger: Constructing a fresh BufferVec/RawBufferVec, pushing values, and calling write_buffer_range before any write_buffer(render_device, queue) or reserve(n, render_device) call created the GPU buffer.

Common situations: New render features with lazily-populated buffers; system ordering where a partial-update system runs before the buffer-creating system; buffers reused across frames but cleared in a way that drops the GPU buffer.

Related errors


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