Repository navigation
[WebGPU] Plugin EP terminates the host process when a Memcpy input fails to convert to a Tensor #33557
Description
Describe the issue
Using the WebGPU plugin EP from C#, selecting the second OrtEpDevice reported by GetEpDevices() and running any model aborts the host process during Run:
Thread 21 Crashed:: .NET TP Worker
4 libc++abi __abort_message
5 libc++abi demangling_terminate_handler()
6 libobjc _objc_terminate()
7 libc++abi std::__terminate(void (*)())
8 libc++abi std::terminate()
9 libonnxruntime_providers_webgpu.dylib onnxruntime::ep::adapter::CreateTensorFromApiValue(OrtValue*) + 904
10 libonnxruntime_providers_webgpu.dylib onnxruntime::ep::adapter::OpKernelContext::Input<onnxruntime::Tensor, void>(int) const + 188
11 libonnxruntime_providers_webgpu.dylib onnxruntime::webgpu::Memcpy::Compute(onnxruntime::ep::adapter::OpKernelContext*) const + 40
12 libonnxruntime_providers_webgpu.dylib onnxruntime::ep::adapter::KernelImpl::ComputeImpl(OrtKernelImpl*, OrtKernelContext*) + 80
13 libonnxruntime.dylib onnxruntime::PluginEpOpKernel::Compute(onnxruntime::OpKernelContext*) const + 28
14 libonnxruntime.dylib onnxruntime::ExecuteKernel(onnxruntime::StreamExecutionContext&, unsigned long, unsigned long, bool const&, onnxruntime::SessionScope&) + 360
Exception Type: EXC_CRASH (SIGABRT), Termination Reason: Namespace SIGNAL, Code 6, Application Specific Information: abort() called.
The same model, same process, device index 0 works; index 1 aborts every time. It is deterministic, not a race.
Environment
- macOS (arm64), Apple Silicon. Crash report from macOS 26/27.
Microsoft.ML.OnnxRuntime1.31.0 +Microsoft.ML.OnnxRuntime.EP.WebGpu0.4.0 (onnxruntime_providers_webgpu.dylibreports1.30.0.20260916.3.f2c39fe).- C# API. Session built with
sessionOptions.AppendExecutionProvider(env, new[] { epDevice }, new Dictionary<string, string>()), thenInferenceSession.Run.
What I think is wrong
adapter::KernelImpl::ComputeImpl in include/onnxruntime/ep/adapter/op_kernel.h is ORT_API_CALL ... noexcept and wraps the kernel call in ORT_TRY / ORT_CATCH(const std::exception&), so a std::exception thrown while converting an input should have become a Status rather than a terminate. Since the process died with std::__terminate, the throw either is not a std::exception, or it crosses a noexcept boundary before the handler can see it.
The throwing code path is Memcpy::Compute -> OpKernelContext::Input<Tensor>() -> CreateTensorFromApiValue(), and the only enforce in that helper is EP_ENFORCE(value.IsTensor(), "Only tensor OrtValue is supported.") (include/onnxruntime/ep/adapter/tensor_helper.h). So on this device the input OrtValue apparently fails that check (or the conversion fails right after it), and instead of a Status the host process is killed.
For a plugin EP this is a hard failure mode: host applications cannot catch it, cannot fall back to CPU, and get no message. Compare #32315, which fixed the same class of problem for the data transfer callback by converting throws into OrtStatus.
Possible duplicate device enumeration
GetEpDevices() returns two WebGpuExecutionProvider devices on this machine even though Apple Silicon exposes one GPU. On Windows I see the same shape with a single NVIDIA GPU listed twice: identical description string, different LUID / DxgiAdapterNumber.
If the second entry is not actually usable, the EP should either not report it or reject it with a Status - reporting it and then aborting when it is used means a host's device picker can kill the process.
Questions
- Is the extra
OrtEpDevicesupposed to be usable? If it exists for a reason (e.g. one entry per adapter view), what should a host use to tell them apart? - Should
ComputeImpluse a catch-all instead ofORT_CATCH(const std::exception&)so an adapter-level enforce can never terminate the host? - Is this fixed in a plugin build newer than 0.4.0? I can retest with a specific version or a dev build.
I can provide the full crash report, and the device metadata from OrtEpDevice.HardwareDevice.Metadata for both entries, if that helps.
Activity
macOS: log every WebGPU device at registration
edgchen1 commentedon Oct 23, 2026
thanks for reporting the issue.
this would be helpful. would be interested to see any additional info about the
OrtEpDevices.edgchen1 commentedon Oct 23, 2026
to answer your questions:
there are a few kinds of
OrtEpDeviceadded by the WebGPUOrtEpFactory. see GetSupportedDevicesImpl.the first kind is associated with an
OrtHardwareDevicecorresponding to a GPU device. any of these should be usable. note that selecting a particular device to use with WebGPU is done separately through provider options, somewhat unintuitively.the second kind is a fallback
OrtEpDevicethat is associated with the CPU device, enabled when the EP factory is configured to allow software adapters. this is currently configured by setting an environment variable,ORT_WEBGPU_EP_ALLOW_SOFTWARE_ADAPTER. it allows use of WebGPU EP with something like the SwiftShader software Vulkan implementation.the third kind is associated with a virtual device. these aren't usable for running inference. the
OrtHardwareDeviceshould have a metadata entry indicating it is a virtual device.not sure yet. wouldn't think we should be throwing anything not derived from
std::exception.EP_ENFORCE()throwsstd::runtime_error.I'm not aware of a fix.
macOS: log every WebGPU device at registration
KakaruHayate commentedon Oct 25, 2026
Thanks for the pointers - the three kinds explained a lot, and one of them let me rule something out. Here is the data you asked for.
Device metadata, Windows x64
Microsoft.ML.OnnxRuntime1.31.0 +Microsoft.ML.OnnxRuntime.EP.WebGpu0.4.0,OrtEnv.GetEpDevices():Neither entry carries
is_virtual(allow_virtual_devicesis off), and the fallback CPU-backed entry is absent (a real GPU device was added), so both are the "first kind" - GPU-associated devices.The difference between them is
DxgiAdapterNumber0 vs 1,DxgiHighPerformanceIndex0 vs 2, and entry 1 has noDiscretemetadata.Both entries are usable on Windows
This is the part that made me doubt the "second device is broken" theory: on the same machine both index 0 and index 1 load the same 272 MB DiffSinger acoustic model without problems (8.0 s on index 1). So duplicate GPU-associated
OrtEpDevices are not harmful per se.macOS is different
On the Mac from the crash report (Apple Silicon, macOS 26/27) the picker also shows two entries, and there the second one aborts during
Run(theMemcpy::Compute/CreateTensorFromApiValue/std::terminatestack from the report), while the first renders fine. Another user reports the opposite (only the second works), so which index is usable differs per machine.Questions
Is one physical GPU expected to produce two GPU-associated
OrtHardwareDevices with different LUIDs, the second one lackingDiscrete? On Windows this host also has a virtual display driver installed, and OpenUtau now filters indirect display adapters on its side (fix: Exclude indirect display adapters openutau/OpenUtau#2549), but these two are reported as ordinary NVIDIA GPUs by ORT. IfDxgiAdapterNumber=1is a render-only view of the same physical adapter, should a host prefer/ignore one of them?On the exception path:
EP_ENFORCEthrowingstd::runtime_errorshould be caught byORT_CATCH(const std::exception&)inadapter::KernelImpl::ComputeImpl. Since the plugin build we ship still reachesstd::__terminate, I suspect the throw crosses anoexceptboundary before reaching that handler. Is the WebGPU plugin library built with exceptions enabled? If it is, would a catch-all inComputeImpl(instead of onlystd::exception) be acceptable as a belt-and-braces measure, so an adapter-level enforce can never take down the host?I have the full
.ipscrash report from the macOS machine (symbolicated stack, thread 21). I cannot attach files through the API, so tell me if you want it and I will ask the reporter to attach it here, or paste whichever sections are useful.macOS: replace CoreML with the ONNX Runtime 1.31 WebGPU plugin EP (#2553
gyagp commentedon Oct 27, 2026
Kakaru (@KakaruHayate) Thanks for reporting this bug. I tried on the macOS, and hopefully #33634 can fix your issue.
On my macOS, it doesn't report multiple devices. But please note device is a logical concept, and one physical adapter may map to multiple devices.
Please also note WebGPU EP doesn't respect your selection from GetEpDevices() now. We'll better handle this part in the near future.