gfx-rs/wgpu · critical
Failed to get mapped range for staging belt buffer
Error message
Failed to get mapped range for staging belt buffer
What it means
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.
Source
Thrown at wgpu/src/util/belt.rs:164
assert!(
offset.is_multiple_of(COPY_BUFFER_ALIGNMENT),
"StagingBelt::write_buffer() offset {offset} must be a multiple of `COPY_BUFFER_ALIGNMENT`"
);
let slice_of_belt = self.allocate(
size,
const { BufferSize::new(crate::COPY_BUFFER_ALIGNMENT).unwrap() },
);
encoder.copy_buffer_to_buffer(
slice_of_belt.buffer(),
slice_of_belt.offset(),
target,
offset,
size.get(),
);
slice_of_belt
.get_mapped_range_mut()
.expect("Failed to get mapped range for staging belt buffer")
}
/// Allocate a staging belt slice with the given `size` and `alignment` and return it.
///
/// `size` must be a multiple of [`COPY_BUFFER_ALIGNMENT`]
/// (as is required by the underlying buffer operations).
///
/// To use this slice, call [`BufferSlice::get_mapped_range_mut()`] and write your data into
/// that [`BufferViewMut`].
/// (The view must be dropped before [`StagingBelt::finish()`] is called.)
///
/// You can then record your own GPU commands to perform with the slice,
/// such as copying it to a texture (whereas
/// [`StagingBelt::write_buffer()`] can only write to other buffers).
/// All commands involving this slice must be submitted after
/// [`StagingBelt::finish()`] is called and before [`StagingBelt::recall()`] is called.
///
/// If the `size` is greater than the space available in any free internal buffer, a new bufferView on GitHub (pinned to 3e11ff59bf)
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
Example fix
// before
let bytes = belt.write_buffer(
&encoder, &dest, offset, &size, &device,
);
bytes.copy_from_slice(data); // panics if device was lost
// after
if device.is_lost() {
(device, belt) = recreate_device_and_belt(adapter);
}
let bytes = belt.write_buffer(
&encoder, &dest, offset, &size, &device,
);
bytes.copy_from_slice(data); Defensive patterns
Strategy: validation
Validate before calling
// before each frame's uploads:
if device.is_lost() {
// recreate device + belt instead of writing to the belt
(device, belt) = recreate();
}
assert!(!device.is_lost(), "device lost; cannot map staging belt"); Type guard
fn belt_usable(device: &Device) -> bool {
!device.is_lost()
} Try / catch
// map failures here surface as a Rust panic (expect), not a Result.
// In wasm/browser setups you can observe the underlying cause via the
// uncaptured error handler installed at device creation:
device.on_uncaptured_error(Box::new(|e| {
log::error!("device error: {e:?}");
device_lost.store(true, Ordering::SeqCst);
}));
// then check device_lost before calling belt.write_buffer. Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- source slice length ({src_len}) does not match destination s
- mid > len
- split_off() requires a one-sided range
- Unexpected error: constructor={constructor} value={value}
- {:?} is not enabled for this backend
AI-assisted analysis of gfx-rs/wgpu@3e11ff59bf (2026-09-03).
Data as JSON: /api/errors/2a46fafde386c828.
Report an issue: GitHub.