{"record":{"id":"2a46fafde386c828","repo":"gfx-rs/wgpu","slug":"failed-to-get-mapped-range-for-staging-belt-buffer","errorCode":null,"errorMessage":"Failed to get mapped range for staging belt buffer","messagePattern":"Failed to get mapped range for staging belt buffer","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"wgpu/src/util/belt.rs","lineNumber":164,"sourceCode":"        assert!(\n            offset.is_multiple_of(COPY_BUFFER_ALIGNMENT),\n            \"StagingBelt::write_buffer() offset {offset} must be a multiple of `COPY_BUFFER_ALIGNMENT`\"\n        );\n\n        let slice_of_belt = self.allocate(\n            size,\n            const { BufferSize::new(crate::COPY_BUFFER_ALIGNMENT).unwrap() },\n        );\n        encoder.copy_buffer_to_buffer(\n            slice_of_belt.buffer(),\n            slice_of_belt.offset(),\n            target,\n            offset,\n            size.get(),\n        );\n        slice_of_belt\n            .get_mapped_range_mut()\n            .expect(\"Failed to get mapped range for staging belt buffer\")\n    }\n\n    /// Allocate a staging belt slice with the given `size` and `alignment` and return it.\n    ///\n    /// `size` must be a multiple of [`COPY_BUFFER_ALIGNMENT`]\n    /// (as is required by the underlying buffer operations).\n    ///\n    /// To use this slice, call [`BufferSlice::get_mapped_range_mut()`] and write your data into\n    /// that [`BufferViewMut`].\n    /// (The view must be dropped before [`StagingBelt::finish()`] is called.)\n    ///\n    /// You can then record your own GPU commands to perform with the slice,\n    /// such as copying it to a texture (whereas\n    /// [`StagingBelt::write_buffer()`] can only write to other buffers).\n    /// All commands involving this slice must be submitted after\n    /// [`StagingBelt::finish()`] is called and before [`StagingBelt::recall()`] is called.\n    ///\n    /// If the `size` is greater than the space available in any free internal buffer, a new buffer","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/gfx-rs/wgpu/blob/3e11ff59bf3f9795d285ecc045014089640d7248/wgpu/src/util/belt.rs#L146-L182","documentation":"StagingBelt::write_buffer returns a slice by mapping the belt's internal staging buffer with get_mapped_range_mut(); this expect fires if the WebGPU implementation refuses the map. Since the belt only ever maps buffers it created as MAP_READ|MAP_WRITE, this indicates the device or buffer is no longer in a mappable state (device lost/destroyed or a driver/map failure), rather than a normal user error.","triggerScenarios":"Calling belt.write_buffer (or via belt.recall/Encoder use) after the Device was lost or destroyed; the belt's chunk buffer was somehow unmapped or the device was dropped while the belt was still in use; an underlying WebGPU/driver map failure on the internal chunk buffer.","commonSituations":"Continuing to upload data through a StagingBelt after handling a device-loss event (uncaptured error callback, device.poll returning lost); holding a belt across a device re-creation on adapter reset; teardown-order bugs where the device is destroyed while frame upload code still runs.","solutions":["Recreate the Device (and a fresh StagingBelt) if the device was lost or destroyed, then retry the upload","Guard upload paths: stop using the belt after receiving an uncaptured-error / device-lost callback","Verify the StagingBelt is only used with buffers/encoders from the same live Device that created it","Check teardown order so the device outlives all belt writes; drop the belt before the device","If it reproduces on a specific platform/driver, update drivers and wgpu version; report a bug since the belt's own buffer should always be mappable"],"exampleFix":"// before\nlet bytes = belt.write_buffer(\n    &encoder, &dest, offset, &size, &device,\n);\nbytes.copy_from_slice(data); // panics if device was lost\n// after\nif device.is_lost() {\n    (device, belt) = recreate_device_and_belt(adapter);\n}\nlet bytes = belt.write_buffer(\n    &encoder, &dest, offset, &size, &device,\n);\nbytes.copy_from_slice(data);","handlingStrategy":"validation","validationCode":"// before each frame's uploads:\nif device.is_lost() {\n    // recreate device + belt instead of writing to the belt\n    (device, belt) = recreate();\n}\nassert!(!device.is_lost(), \"device lost; cannot map staging belt\");","typeGuard":"fn belt_usable(device: &Device) -> bool {\n    !device.is_lost()\n}","tryCatchPattern":"// map failures here surface as a Rust panic (expect), not a Result.\n// In wasm/browser setups you can observe the underlying cause via the\n// uncaptured error handler installed at device creation:\ndevice.on_uncaptured_error(Box::new(|e| {\n    log::error!(\"device error: {e:?}\");\n    device_lost.store(true, Ordering::SeqCst);\n}));\n// then check device_lost before calling belt.write_buffer.","preventionTips":["Check device.is_lost() before staging-belt writes, especially after resize/reset events","Keep StagingBelt, Device, and Queue created from the same instance and used together","Drop the belt before the device during teardown","Handle uncaptured error / device-loss callbacks by halting upload paths immediately","Keep wgpu and drivers up to date; a belt-internal map failure usually indicates device loss or a platform bug"],"tags":["webgpu","staging-belt","buffer-mapping","panic","device-loss"],"backgroundTag":"buffer-map-failed","analyzedSha":"3e11ff59bf3f9795d285ecc045014089640d7248","analyzedAt":"2026-09-03T01:43:21.459Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T07:17:11.731Z"}