{"record":{"id":"89206f72ab0b561e","repo":"denoland/deno","slug":"validation-error-occurred","errorCode":null,"errorMessage":"validation error occurred","messagePattern":"validation error occurred","errorType":"exception","errorClass":"DOMExceptionOperationError","httpStatus":null,"severity":"error","filePath":"ext/webgpu/buffer.rs","lineNumber":235,"sourceCode":"        sender.send(status).unwrap();\n      });\n\n      let err = self\n        .instance\n        .buffer_map_async(\n          self.id,\n          offset,\n          size,\n          wgpu_core::resource::BufferMapOperation {\n            host: mode,\n            callback: Some(callback),\n          },\n        )\n        .err();\n\n      if err.is_some() {\n        self.error_handler.push_error(err);\n        return Err(BufferError::Operation(\"validation error occurred\"));\n      }\n    }\n\n    let done = Rc::new(RefCell::new(false));\n    let done_ = done.clone();\n    let device_poll_fut = async move {\n      while !*done.borrow() {\n        {\n          self\n            .instance\n            .device_poll(self.device, wgpu_types::PollType::wait_indefinitely())\n            .unwrap();\n        }\n        tokio::time::sleep(Duration::from_millis(10)).await;\n      }\n      Ok::<(), BufferError>(())\n    };\n","sourceCodeStart":217,"sourceCodeEnd":253,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/webgpu/buffer.rs#L217-L253","documentation":"GPUBuffer.mapAsync() in Deno's WebGPU maps the buffer via wgpu_core::buffer_map_async; if that call returns a validation error, buffer.rs maps it to BufferError::Operation(\"validation error occurred\") and rejects the mapAsync promise (the error is also pushed to the device's uncaptured-error handler). Typical validation failures: offset/size not aligned, size out of bounds, buffer created without MAP usage, or the buffer/device state being invalid.","triggerScenarios":"`await buffer.mapAsync(GPUMapMode.READ, offset, size)` where offset or size is not a multiple of 8, offset+size exceeds buffer.byteLength, the GPUBuffer was created without GPUBufferUsage.MAP_READ/MAP_WRITE, mapAsync is called while already mapped/pending, or the device was lost/destroyed. Example: creating a buffer with COPY_DST only and then trying to map it for reading.","commonSituations":"Forgetting MAP_READ|MAP_WRITE usage flags and mapping anyway (most common); passing byte offsets computed from unaligned struct sizes; reusing offset math meant for copyBufferToBuffer (4-byte alignment) where mapping requires 8; racing two mapAsync calls on the same buffer; continuing after device loss in error-handling tests.","solutions":["Create the buffer with the right flags: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST for readback, GPUBufferUsage.MAP_WRITE | GPUBufferUsage.COPY_SRC for upload.","Align offset and size to 8 bytes (round down/up) and clamp size to buffer.byteLength - offset.","Ensure the buffer is unmapped (getMappedRange + unmap finished) before calling mapAsync again.","Catch the rejection, and also register device.onuncapturederror / device.lost handling to surface the underlying wgpu validation message."],"exampleFix":"// before\nconst buf = device.createBuffer({\n  size: 16,\n  usage: GPUBufferUsage.COPY_DST, // no MAP_READ -> \"validation error occurred\"\n});\nawait buf.mapAsync(GPUMapMode.READ, 0);\n\n// after\nconst buf = device.createBuffer({\n  size: 16,\n  usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,\n});\nawait buf.mapAsync(GPUMapMode.READ, 0, 16);","handlingStrategy":"try-catch","validationCode":"const MAP_ALIGNMENT = 8;\nfunction assertMappable(buffer, offset, size) {\n  const usage = buffer.usage ?? (buffer._creationUsage as GPUBufferUsageFlags);\n  const s = size ?? buffer.byteLength - offset;\n  if (offset % MAP_ALIGNMENT !== 0 || s % MAP_ALIGNMENT !== 0) {\n    throw new Error(`mapAsync offset/size must be ${MAP_ALIGNMENT}-byte aligned`);\n  }\n  if (offset + s > buffer.byteLength) throw new Error(\"mapAsync range out of bounds\");\n  return s;\n}","typeGuard":"function isMappableBuffer(buffer: GPUBuffer, mode: GPUMapModeFlags): boolean {\n  const u = buffer.usage;\n  return mode === GPUMapMode.READ\n    ? (u & GPUBufferUsage.MAP_READ) !== 0\n    : (u & GPUBufferUsage.MAP_WRITE) !== 0;\n}","tryCatchPattern":"try {\n  await buffer.mapAsync(GPUMapMode.READ, alignedOffset, alignedSize);\n} catch (err) {\n  console.error(\"mapAsync failed:\", err.message); // \"validation error occurred\"\n  // recreate buffer with MAP flags / fix alignment, or bail\n}\ndevice.onuncapturederror = (e) => console.error(\"uncaptured:\", e.error.message);","preventionTips":["Always create readback buffers with MAP_READ | COPY_DST and upload buffers with MAP_WRITE | COPY_SRC.","Align map offsets/sizes to 8 bytes; do not reuse 4-byte copyBufferToBuffer math for mapping.","Never call mapAsync while a previous map is pending; unmap before remapping.","Log device uncaptured errors to see the underlying wgpu validation reason behind the generic message."],"tags":["webgpu","gpu-buffer","mapasync","validation","alignment","usage-flags"],"backgroundTag":"webgpu-validation-error","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}