From 340f948a5f679998cbc0d00e1da6b5b21b2a3ec8 Mon Sep 17 00:00:00 2001 From: alinpahontu2912 Date: Fri, 20 Mar 2026 13:05:26 +0100 Subject: [PATCH 1/4] Fix native library loader silently failing when CUDA DLLs are missing Change all 'ok = TryLoadNativeLibraryByName(...)' calls to 'ok &= ...' so that failures accumulate instead of being overwritten by subsequent successful loads. Initialize 'ok = true' before the loading chain. Previously, each load call overwrote the result of the previous one, so if an early CUDA dependency (e.g. cudnn_adv64_9) failed to load but LibTorchSharp succeeded, 'ok' would be true. This caused: - nativeBackendCudaLoaded set to true despite missing dependencies - The fallback loading path was skipped - The diagnostic trace (StringBuilder) was discarded - Subsequent load attempts were skipped entirely - CUDA operations failed later with cryptic errors Now any single load failure keeps 'ok' as false, ensuring the fallback path is attempted and the full diagnostic trace is preserved in error messages. Fixes dotnet/TorchSharp#1545 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/TorchSharp/Torch.cs | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/TorchSharp/Torch.cs b/src/TorchSharp/Torch.cs index dd7a07689..7e8a33fd7 100644 --- a/src/TorchSharp/Torch.cs +++ b/src/TorchSharp/Torch.cs @@ -116,7 +116,7 @@ private static void LoadNativeBackend(bool useCudaBackend, out StringBuilder? tr trace = null; if (!alreadyLoaded) { - bool ok; + bool ok = true; trace = new StringBuilder(); trace.AppendLine($""); trace.AppendLine($"TorchSharp: LoadNativeBackend: Initialising native backend, useCudaBackend = {useCudaBackend}"); @@ -134,27 +134,27 @@ private static void LoadNativeBackend(bool useCudaBackend, out StringBuilder? tr // Preloading these DLLs on windows seems to iron out problems where one native DLL // requests a load of another through dynamic linking techniques. // - ok = TryLoadNativeLibraryByName("cudnn_adv64_9", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("cudnn_cnn64_9", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("cudnn_ops64_9", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("cudnn_graph64_9.dll", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("cudnn_heuristic64_9.dll", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("cudnn_engines_precompiled64_9.dll", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("cudnn_engines_runtime_compiled64_9.dll", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("nvrtc-builtins64_128", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("caffe2_nvrtc", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("nvrtc64_120_0", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("cublasLt64_12", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("cufft64_11", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("cusparse64_12", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("cusolver64_11", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("cudnn_adv64_9", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("cudnn_cnn64_9", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("cudnn_ops64_9", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("cudnn_graph64_9.dll", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("cudnn_heuristic64_9.dll", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("cudnn_engines_precompiled64_9.dll", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("cudnn_engines_runtime_compiled64_9.dll", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("nvrtc-builtins64_128", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("caffe2_nvrtc", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("nvrtc64_120_0", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("cublasLt64_12", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("cufft64_11", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("cusparse64_12", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("cusolver64_11", typeof(torch).Assembly, trace); } - ok = TryLoadNativeLibraryByName("torch_cuda", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("LibTorchSharp", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("torch_cuda", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("LibTorchSharp", typeof(torch).Assembly, trace); } else { - ok = TryLoadNativeLibraryByName("torch_cpu", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("LibTorchSharp", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("torch_cpu", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("LibTorchSharp", typeof(torch).Assembly, trace); } trace.AppendLine($" Result from regular native load of LibTorchSharp is {ok}"); From c7f1271edcffe64b6e53fb566ab2cb8d559f54a2 Mon Sep 17 00:00:00 2001 From: alinpahontu2912 Date: Fri, 20 Mar 2026 13:05:26 +0100 Subject: [PATCH 2/4] Fix native library loader silently failing when CUDA DLLs are missing Change all 'ok = TryLoadNativeLibraryByName(...)' calls to 'ok &= ...' so that failures accumulate instead of being overwritten by subsequent successful loads. Initialize 'ok = true' before the loading chain. Previously, each load call overwrote the result of the previous one, so if an early CUDA dependency (e.g. cudnn_adv64_9) failed to load but LibTorchSharp succeeded, 'ok' would be true. This caused: - nativeBackendCudaLoaded set to true despite missing dependencies - The fallback loading path was skipped - The diagnostic trace (StringBuilder) was discarded - Subsequent load attempts were skipped entirely - CUDA operations failed later with cryptic errors Now any single load failure keeps 'ok' as false, ensuring the fallback path is attempted and the full diagnostic trace is preserved in error messages. Fixes dotnet/TorchSharp#1545 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/TorchSharp/Torch.cs | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/TorchSharp/Torch.cs b/src/TorchSharp/Torch.cs index dd7a07689..7e8a33fd7 100644 --- a/src/TorchSharp/Torch.cs +++ b/src/TorchSharp/Torch.cs @@ -116,7 +116,7 @@ private static void LoadNativeBackend(bool useCudaBackend, out StringBuilder? tr trace = null; if (!alreadyLoaded) { - bool ok; + bool ok = true; trace = new StringBuilder(); trace.AppendLine($""); trace.AppendLine($"TorchSharp: LoadNativeBackend: Initialising native backend, useCudaBackend = {useCudaBackend}"); @@ -134,27 +134,27 @@ private static void LoadNativeBackend(bool useCudaBackend, out StringBuilder? tr // Preloading these DLLs on windows seems to iron out problems where one native DLL // requests a load of another through dynamic linking techniques. // - ok = TryLoadNativeLibraryByName("cudnn_adv64_9", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("cudnn_cnn64_9", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("cudnn_ops64_9", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("cudnn_graph64_9.dll", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("cudnn_heuristic64_9.dll", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("cudnn_engines_precompiled64_9.dll", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("cudnn_engines_runtime_compiled64_9.dll", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("nvrtc-builtins64_128", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("caffe2_nvrtc", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("nvrtc64_120_0", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("cublasLt64_12", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("cufft64_11", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("cusparse64_12", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("cusolver64_11", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("cudnn_adv64_9", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("cudnn_cnn64_9", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("cudnn_ops64_9", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("cudnn_graph64_9.dll", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("cudnn_heuristic64_9.dll", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("cudnn_engines_precompiled64_9.dll", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("cudnn_engines_runtime_compiled64_9.dll", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("nvrtc-builtins64_128", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("caffe2_nvrtc", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("nvrtc64_120_0", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("cublasLt64_12", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("cufft64_11", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("cusparse64_12", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("cusolver64_11", typeof(torch).Assembly, trace); } - ok = TryLoadNativeLibraryByName("torch_cuda", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("LibTorchSharp", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("torch_cuda", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("LibTorchSharp", typeof(torch).Assembly, trace); } else { - ok = TryLoadNativeLibraryByName("torch_cpu", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("LibTorchSharp", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("torch_cpu", typeof(torch).Assembly, trace); + ok &= TryLoadNativeLibraryByName("LibTorchSharp", typeof(torch).Assembly, trace); } trace.AppendLine($" Result from regular native load of LibTorchSharp is {ok}"); From f1c93e4f7e054edd67e5b9636122a8f7c2deb23c Mon Sep 17 00:00:00 2001 From: Stefan-Alin Pahontu <56953855+alinpahontu2912@users.noreply.github.com> Date: Fri, 20 Mar 2026 16:33:32 +0100 Subject: [PATCH 3/4] Update src/TorchSharp/Torch.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/TorchSharp/Torch.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TorchSharp/Torch.cs b/src/TorchSharp/Torch.cs index 7e8a33fd7..076efed8c 100644 --- a/src/TorchSharp/Torch.cs +++ b/src/TorchSharp/Torch.cs @@ -157,7 +157,7 @@ private static void LoadNativeBackend(bool useCudaBackend, out StringBuilder? tr ok &= TryLoadNativeLibraryByName("LibTorchSharp", typeof(torch).Assembly, trace); } - trace.AppendLine($" Result from regular native load of LibTorchSharp is {ok}"); + trace.AppendLine($" Result from regular native load of backend libraries (CUDA preloads + torch_* + LibTorchSharp) is {ok}"); // Try dynamic load from package directories if (!ok) { From 28363703bf782bd756f6eb0c69cf9ee789fb0eb7 Mon Sep 17 00:00:00 2001 From: alinpahontu2912 Date: Tue, 24 Mar 2026 13:34:02 +0100 Subject: [PATCH 4/4] Fix native loader: only LibTorchSharp determines load success The previous fix changed simple assignments (ok =) to compound AND (ok &=) for all native library loads. This broke CPU-only systems because torch_cuda failing would permanently set ok=false, even when LibTorchSharp loaded successfully, triggering a NotSupportedException. Changes: - CUDA preloads (cudnn, nvrtc, etc.): fire-and-forget, failures are non-fatal and only logged via trace - torch_cuda / torch_cpu: fire-and-forget warmup loads for dependency resolution; their result doesn't gate success - LibTorchSharp: the only critical load that determines ok - TryInitializeDeviceType: catch NotSupportedException to prevent static constructor crashes - Static constructor: fall back to CPU backend when CUDA is unavailable Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Tensor/Factories/Tensor.Factories.cs | 5 ++- src/TorchSharp/Torch.cs | 45 +++++++++++-------- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/TorchSharp/Tensor/Factories/Tensor.Factories.cs b/src/TorchSharp/Tensor/Factories/Tensor.Factories.cs index b306c0cd7..df758de80 100644 --- a/src/TorchSharp/Tensor/Factories/Tensor.Factories.cs +++ b/src/TorchSharp/Tensor/Factories/Tensor.Factories.cs @@ -647,7 +647,10 @@ private static void ValidateIntegerRange(long value, ScalarType dtype, string ar static torch() { deleters = new ConcurrentDictionary(); - TryInitializeDeviceType(DeviceType.CUDA); + if (!TryInitializeDeviceType(DeviceType.CUDA)) { + // CUDA not available, ensure CPU backend is loaded + LoadNativeBackend(false, out _); + } } #endregion } diff --git a/src/TorchSharp/Torch.cs b/src/TorchSharp/Torch.cs index 076efed8c..e0daf0bd7 100644 --- a/src/TorchSharp/Torch.cs +++ b/src/TorchSharp/Torch.cs @@ -134,27 +134,29 @@ private static void LoadNativeBackend(bool useCudaBackend, out StringBuilder? tr // Preloading these DLLs on windows seems to iron out problems where one native DLL // requests a load of another through dynamic linking techniques. // - ok &= TryLoadNativeLibraryByName("cudnn_adv64_9", typeof(torch).Assembly, trace); - ok &= TryLoadNativeLibraryByName("cudnn_cnn64_9", typeof(torch).Assembly, trace); - ok &= TryLoadNativeLibraryByName("cudnn_ops64_9", typeof(torch).Assembly, trace); - ok &= TryLoadNativeLibraryByName("cudnn_graph64_9.dll", typeof(torch).Assembly, trace); - ok &= TryLoadNativeLibraryByName("cudnn_heuristic64_9.dll", typeof(torch).Assembly, trace); - ok &= TryLoadNativeLibraryByName("cudnn_engines_precompiled64_9.dll", typeof(torch).Assembly, trace); - ok &= TryLoadNativeLibraryByName("cudnn_engines_runtime_compiled64_9.dll", typeof(torch).Assembly, trace); - ok &= TryLoadNativeLibraryByName("nvrtc-builtins64_128", typeof(torch).Assembly, trace); - ok &= TryLoadNativeLibraryByName("caffe2_nvrtc", typeof(torch).Assembly, trace); - ok &= TryLoadNativeLibraryByName("nvrtc64_120_0", typeof(torch).Assembly, trace); - ok &= TryLoadNativeLibraryByName("cublasLt64_12", typeof(torch).Assembly, trace); - ok &= TryLoadNativeLibraryByName("cufft64_11", typeof(torch).Assembly, trace); - ok &= TryLoadNativeLibraryByName("cusparse64_12", typeof(torch).Assembly, trace); - ok &= TryLoadNativeLibraryByName("cusolver64_11", typeof(torch).Assembly, trace); + // These are optional preloads to help Windows resolve transitive native dependencies. + // Failures here are non-fatal -- the critical loads are torch_cuda and LibTorchSharp below. + TryLoadNativeLibraryByName("cudnn_adv64_9", typeof(torch).Assembly, trace); + TryLoadNativeLibraryByName("cudnn_cnn64_9", typeof(torch).Assembly, trace); + TryLoadNativeLibraryByName("cudnn_ops64_9", typeof(torch).Assembly, trace); + TryLoadNativeLibraryByName("cudnn_graph64_9.dll", typeof(torch).Assembly, trace); + TryLoadNativeLibraryByName("cudnn_heuristic64_9.dll", typeof(torch).Assembly, trace); + TryLoadNativeLibraryByName("cudnn_engines_precompiled64_9.dll", typeof(torch).Assembly, trace); + TryLoadNativeLibraryByName("cudnn_engines_runtime_compiled64_9.dll", typeof(torch).Assembly, trace); + TryLoadNativeLibraryByName("nvrtc-builtins64_128", typeof(torch).Assembly, trace); + TryLoadNativeLibraryByName("caffe2_nvrtc", typeof(torch).Assembly, trace); + TryLoadNativeLibraryByName("nvrtc64_120_0", typeof(torch).Assembly, trace); + TryLoadNativeLibraryByName("cublasLt64_12", typeof(torch).Assembly, trace); + TryLoadNativeLibraryByName("cufft64_11", typeof(torch).Assembly, trace); + TryLoadNativeLibraryByName("cusparse64_12", typeof(torch).Assembly, trace); + TryLoadNativeLibraryByName("cusolver64_11", typeof(torch).Assembly, trace); } - ok &= TryLoadNativeLibraryByName("torch_cuda", typeof(torch).Assembly, trace); - ok &= TryLoadNativeLibraryByName("LibTorchSharp", typeof(torch).Assembly, trace); + TryLoadNativeLibraryByName("torch_cuda", typeof(torch).Assembly, trace); + ok = TryLoadNativeLibraryByName("LibTorchSharp", typeof(torch).Assembly, trace); } else { - ok &= TryLoadNativeLibraryByName("torch_cpu", typeof(torch).Assembly, trace); - ok &= TryLoadNativeLibraryByName("LibTorchSharp", typeof(torch).Assembly, trace); + TryLoadNativeLibraryByName("torch_cpu", typeof(torch).Assembly, trace); + ok = TryLoadNativeLibraryByName("LibTorchSharp", typeof(torch).Assembly, trace); } trace.AppendLine($" Result from regular native load of backend libraries (CUDA preloads + torch_* + LibTorchSharp) is {ok}"); @@ -299,7 +301,12 @@ public static bool TryInitializeDeviceType(DeviceType deviceType) return false; } - LoadNativeBackend(deviceType == DeviceType.CUDA, out _); + try { + LoadNativeBackend(deviceType == DeviceType.CUDA, out _); + } catch (NotSupportedException) { + return false; + } + if (deviceType == DeviceType.CUDA) { return cuda.CallTorchCudaIsAvailable(); } else {