bevyengine/bevy · error
Tried to read past the end of the buffer (start: {start}, si
Error message
Tried to read past the end of the buffer (start: {start}, size: {size}, buffer size: {full_size}). What it means
The GPU readback system (driven by entities spawned with `GpuReadbackBundles::buffer_range(handle, offset, size)`) panics during preparation when the requested `start + size` exceeds the actual byte size of the backing `ShaderBuffer` SSBO. It is a host-side bounds check run before the GPU copy is queued.
Source
Thrown at crates/bevy_render/src/gpu_readback.rs:341
size: gpu_image.texture_descriptor.size,
},
buffer,
rx,
tx,
});
}
}
Readback::Buffer {
buffer,
start_offset_and_size,
} => {
if let Some(ssbo) = ssbos.get(buffer) {
let full_size = ssbo.buffer.size();
let size = start_offset_and_size
.map(|(start, size)| {
let end = start + size;
if end > full_size {
panic!(
"Tried to read past the end of the buffer (start: {start}, \
size: {size}, buffer size: {full_size})."
);
}
size
})
.unwrap_or(full_size);
let buffer = buffer_pool.get(&render_device, size);
let (tx, rx) = async_channel::bounded(1);
readbacks.requested.push(GpuReadback {
entity: entity.id(),
src: ReadbackSource::Buffer {
start_offset_and_size: *start_offset_and_size,
buffer: ssbo.buffer.clone(),
},
buffer,
rx,
tx,View on GitHub (pinned to 4805ca792c)
Solutions
- Use `GpuReadbackBundles::buffer(handle)` when you want the whole buffer
- Compute the subrange from live data at spawn time (e.g. `len * size_of::<T>()`) instead of hardcoding
- Clamp size to `buffer_size - start` before spawning the readback entity
Example fix
// before: buffer only holds 1024 bytes commands.spawn(GpuReadbackBundles::buffer_range(handle, 0, 4096)); // after commands.spawn(GpuReadbackBundles::buffer_range(handle, 0, 1024)); // or read the whole buffer: commands.spawn(GpuReadbackBundles::buffer(handle));
Defensive patterns
Strategy: validation
Validate before calling
// before spawning a readback, clamp the range to the buffer's real size
if let Some(buffer) = shader_buffers.get(&handle) {
let full = buffer.buffer().size();
let size = size.min(full.saturating_sub(start));
commands.spawn(GpuReadbackBundles::buffer_range(handle, start, size));
} Prevention
- Prefer buffer(handle) for whole-buffer reads
- Compute byte ranges from live element counts, not constants
- Recompute readback ranges whenever the backing buffer is resized
When it happens
Trigger: Spawning a readback with `buffer_range(handle, start, size)` where `start + size > buffer.size()`: a hardcoded size larger than the buffer, a stale offset/size after the buffer shrank, or offsets computed in the wrong units.
Common situations: Periodic readbacks of a typed SSBO with a fixed byte size while the buffer contents grow/shrink; mixing element counts with byte offsets.
Related errors
- Failed to map buffer
- `Range` provided to `render_range()` is out of bounds
- Cannot call `ReflectComponent::reflect_mut` on component {na
- Cannot call `ReflectComponent::reflect_unchecked_mut` on com
- component should represent a type.
AI-assisted analysis of bevyengine/bevy@4805ca792c (2026-08-20).
Data as JSON: /api/errors/62ef5e1ebc807110.
Report an issue: GitHub.