{"record":{"id":"62ef5e1ebc807110","repo":"bevyengine/bevy","slug":"tried-to-read-past-the-end-of-the-buffer-start","errorCode":null,"errorMessage":"Tried to read past the end of the buffer (start: {start}, size: {size}, buffer size: {full_size}).","messagePattern":"Tried to read past the end of the buffer \\(start: (.+?), size: (.+?), buffer size: (.+?)\\)\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/bevy_render/src/gpu_readback.rs","lineNumber":341,"sourceCode":"                            size: gpu_image.texture_descriptor.size,\n                        },\n                        buffer,\n                        rx,\n                        tx,\n                    });\n                }\n            }\n            Readback::Buffer {\n                buffer,\n                start_offset_and_size,\n            } => {\n                if let Some(ssbo) = ssbos.get(buffer) {\n                    let full_size = ssbo.buffer.size();\n                    let size = start_offset_and_size\n                        .map(|(start, size)| {\n                            let end = start + size;\n                            if end > full_size {\n                                panic!(\n                                    \"Tried to read past the end of the buffer (start: {start}, \\\n                                    size: {size}, buffer size: {full_size}).\"\n                                );\n                            }\n                            size\n                        })\n                        .unwrap_or(full_size);\n                    let buffer = buffer_pool.get(&render_device, size);\n                    let (tx, rx) = async_channel::bounded(1);\n                    readbacks.requested.push(GpuReadback {\n                        entity: entity.id(),\n                        src: ReadbackSource::Buffer {\n                            start_offset_and_size: *start_offset_and_size,\n                            buffer: ssbo.buffer.clone(),\n                        },\n                        buffer,\n                        rx,\n                        tx,","sourceCodeStart":323,"sourceCodeEnd":359,"githubUrl":"https://github.com/bevyengine/bevy/blob/4805ca792c3e94eef07afd6fa9a2712f388c9a67/crates/bevy_render/src/gpu_readback.rs#L323-L359","documentation":"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.","triggerScenarios":"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.","commonSituations":"Periodic readbacks of a typed SSBO with a fixed byte size while the buffer contents grow/shrink; mixing element counts with byte offsets.","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"],"exampleFix":"// before: buffer only holds 1024 bytes\ncommands.spawn(GpuReadbackBundles::buffer_range(handle, 0, 4096));\n\n// after\ncommands.spawn(GpuReadbackBundles::buffer_range(handle, 0, 1024));\n// or read the whole buffer:\ncommands.spawn(GpuReadbackBundles::buffer(handle));","handlingStrategy":"validation","validationCode":"// before spawning a readback, clamp the range to the buffer's real size\nif let Some(buffer) = shader_buffers.get(&handle) {\n    let full = buffer.buffer().size();\n    let size = size.min(full.saturating_sub(start));\n    commands.spawn(GpuReadbackBundles::buffer_range(handle, start, size));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["bevy","gpu-readback","buffer","out-of-bounds","panic"],"backgroundTag":"gpu-buffer-read-out-of-bounds","analyzedSha":"4805ca792c3e94eef07afd6fa9a2712f388c9a67","analyzedAt":"2026-08-20T16:12:39.808Z","contentChangedAt":"2026-08-20T16:12:39.808Z","schemaVersion":2},"datasetVersion":"2026-09-09T01:17:15.007Z"}