From 5f622f6c2ff2d0967e6439f491b2c06c32846282 Mon Sep 17 00:00:00 2001 From: Matt Van Horn Date: Wed, 10 Jun 2026 02:17:21 -0700 Subject: [PATCH] fix: clearer Studio setup error when GPU driver is too old for the installed CUDA toolkit (#5993) * fix: clearer Studio setup error when GPU driver is too old for the installed CUDA toolkit * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Honor optional color arg in setup.sh substep so driver/toolkit warnings render in C_WARN * Add regression test that setup.sh _cuda_version_gt compares numerically for PR #5993 * Update Resolve-CudaToolkit test for the new driver-too-old messaging setup.ps1 now routes the too-new-toolkit case through Write-CudaDriverToolkitMismatch instead of the old 'is installed but INCOMPATIBLE' banner. Extract that helper alongside Resolve-CudaToolkit so the child pwsh can run it, and assert the new driver-too-old guidance (and the one-line source-build error) instead of the removed INCOMPATIBLE text. * Address review nits: document Windows hard-exit asymmetry and add toolkit/driver edge tests setup.ps1: note that only a forced source build reaches the hard-exit branch (the prebuilt path returned above), unlike setup.sh which degrades to CPU. test_selection_logic.py: cover the CUDA UMD Version variant, the empty nvcc version guard, and the too_old (< 12.4) short-circuit. * fix(studio): allow CUDA minor-version compat and try installed toolkits before CPU fallback The driver check now compares CUDA major versions only, per NVIDIA minor-version compatibility, and when the selected nvcc is still too new the setup iterates other installed toolkits and uses the newest driver-compatible one before falling back to a CPU llama.cpp build. Same rule mirrored in setup.ps1. * style: apply ruff kwarg-spacing format after rebase * Accept a same-major CUDA toolkit found only on PATH in the Windows fallback The major-only compatibility fix updated the side-by-side scan (Find-Nvcc -MaxVersion) and the CUDA_PATH check, but the fallback that runs when Find-Nvcc -MaxVersion returns null still recorded any plain Find-Nvcc result as an incompatible toolkit without re-checking the major. A same-major toolkit discoverable only via PATH, process CUDA_PATH, or a custom location (e.g. toolkit 13.3 with a driver supporting CUDA 13.2) was therefore rejected even though it is compatible. Re-apply the same major-only rule in the fallback: use the toolkit when its major is within the driver's, otherwise record it as too-new. Adds a regression test covering the PATH-only same-major case. * Add CUDA driver/toolkit selection edge-case tests for Studio setup * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Tighten comments in Studio CUDA driver/toolkit setup Collapse multi-line comments, drop obvious ones, keep the load-bearing intent (the major-compat invariant, the Windows hard-exit vs setup.sh-CPU asymmetry, the PATH-only fallback rationale). Comment-only; no code change. * Clarify the Windows source-build hard-exit comment The path is reached by any committed source build (forced, or after a prebuilt-install failure), not only a forced one. Comment-only. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Drop two obvious comments in setup.ps1 CUDA detection --------- Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Daniel Han --- studio/setup.ps1 | 77 ++-- studio/setup.sh | 182 ++++++-- tests/studio/install/test_selection_logic.py | 443 +++++++++++++++++++ tests/studio/test_resolve_cuda_toolkit.ps1 | 69 ++- 4 files changed, 685 insertions(+), 86 deletions(-) diff --git a/studio/setup.ps1 b/studio/setup.ps1 index 4d47010136..920d1af13a 100644 --- a/studio/setup.ps1 +++ b/studio/setup.ps1 @@ -220,14 +220,10 @@ function Get-InstalledLlamaPrebuiltRelease { function Find-Nvcc { param([string]$MaxVersion = "") - # If MaxVersion is set, we need to find a toolkit <= that version. - # CUDA toolkits install side-by-side under C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\vX.Y\ - $toolkitBase = 'C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA' if ($MaxVersion -and (Test-Path $toolkitBase)) { $drMajor = [int]$MaxVersion.Split('.')[0] - $drMinor = [int]$MaxVersion.Split('.')[1] # Get all installed CUDA dirs, sorted descending (highest first) $cudaDirs = Get-ChildItem -Directory $toolkitBase | Where-Object { @@ -236,8 +232,8 @@ function Find-Nvcc { foreach ($dir in $cudaDirs) { if ($dir.Name -match '^v(\d+)\.(\d+)') { - $tkMajor = [int]$Matches[1]; $tkMinor = [int]$Matches[2] - $compatible = ($tkMajor -lt $drMajor) -or ($tkMajor -eq $drMajor -and $tkMinor -le $drMinor) + $tkMajor = [int]$Matches[1] + $compatible = ($tkMajor -le $drMajor) if ($compatible) { $nvcc = Join-Path $dir.FullName 'bin\nvcc.exe' if (Test-Path $nvcc) { @@ -278,6 +274,19 @@ function Find-Nvcc { return $null } +function Write-CudaDriverToolkitMismatch { + param( + [Parameter(Mandatory = $true)][string]$ToolkitVersion, + [Parameter(Mandatory = $true)][string]$DriverMaxCuda, + [string]$Color = "Yellow" + ) + $toolkitMajor = $ToolkitVersion.Split('.')[0] + $driverMajor = $DriverMaxCuda.Split('.')[0] + substep "CUDA Toolkit $ToolkitVersion is a major-version mismatch: toolkit major $toolkitMajor exceeds driver CUDA major $driverMajor ($DriverMaxCuda)." $Color + substep "Update the NVIDIA GPU driver to run CUDA Toolkit $ToolkitVersion, or install a CUDA $driverMajor.x toolkit." $Color + substep "Or let Studio use the prebuilt CUDA bundle; it does not need the local toolkit." $Color +} + # Detect CUDA Compute Capability via nvidia-smi. # Returns e.g. "80" for A100 (8.0), "89" for RTX 4090 (8.9), etc. # Returns $null if detection fails. @@ -1062,17 +1071,13 @@ if ($vsResult) { # or installed. Without it, detection is best-effort and only sets the flag. function Resolve-CudaToolkit { param([switch]$RequireOrExit) -# IMPORTANT: The CUDA Toolkit version must be <= the max CUDA version the -# NVIDIA driver supports. nvidia-smi reports this as "CUDA Version: X.Y". -# If we install a toolkit newer than the driver supports, llama-server will -# fail at runtime with "ggml_cuda_init: failed to initialize CUDA: (null)". +# Toolkit major must be <= the driver's max CUDA major (nvidia-smi "CUDA Version: X.Y"); +# a newer-major toolkit fails at runtime ("ggml_cuda_init: failed to initialize CUDA"). -# -- Detect max CUDA version the driver supports -- $DriverMaxCuda = $null try { $smiOut = & $NvidiaSmiExe 2>&1 | Out-String - # Newer NVIDIA drivers (e.g. 610.x) report the driver max CUDA as - # "CUDA UMD Version: X.Y" rather than "CUDA Version: X.Y"; accept both. + # Newer drivers report "CUDA UMD Version: X.Y" instead of "CUDA Version: X.Y"; accept both. if ($smiOut -match "CUDA(?: UMD)? Version:\s+([\d]+)\.([\d]+)") { $DriverMaxCuda = "$($Matches[1]).$($Matches[2])" substep "driver supports up to CUDA $DriverMaxCuda" @@ -1096,7 +1101,6 @@ $NvccPath = $null if ($DriverMaxCuda) { $drMajorCuda = [int]$DriverMaxCuda.Split('.')[0] - $drMinorCuda = [int]$DriverMaxCuda.Split('.')[1] # --- Step 1: Check existing CUDA_PATH first --- $existingCudaPath = [Environment]::GetEnvironmentVariable('CUDA_PATH', 'Machine') @@ -1108,7 +1112,7 @@ if ($DriverMaxCuda) { $verOut = & $candidateNvcc --version 2>&1 | Out-String if ($verOut -match 'release\s+(\d+)\.(\d+)') { $tkMaj = [int]$Matches[1]; $tkMin = [int]$Matches[2] - $isCompat = ($tkMaj -lt $drMajorCuda) -or ($tkMaj -eq $drMajorCuda -and $tkMin -le $drMinorCuda) + $isCompat = ($tkMaj -le $drMajorCuda) if ($isCompat) { # Also verify the toolkit supports our GPU architecture $archOk = $true @@ -1124,7 +1128,7 @@ if ($DriverMaxCuda) { substep "using existing CUDA Toolkit at CUDA_PATH (nvcc: $NvccPath)" } } else { - substep "CUDA_PATH ($existingCudaPath) has CUDA $tkMaj.$tkMin which exceeds driver max $DriverMaxCuda" "Yellow" + substep "CUDA_PATH ($existingCudaPath) has CUDA $tkMaj.$tkMin with major $tkMaj, which exceeds driver CUDA major $drMajorCuda ($DriverMaxCuda)" "Yellow" } } } @@ -1141,12 +1145,19 @@ if ($DriverMaxCuda) { } } } else { - # Check if there's an incompatible (too new) toolkit installed + # No side-by-side match: a major-compatible toolkit may still be on + # PATH/CUDA_PATH/a custom dir; use it, else record it as too-new. $AnyNvcc = Find-Nvcc if ($AnyNvcc) { $NvccOut = & $AnyNvcc --version 2>&1 | Out-String - if ($NvccOut -match "release\s+([\d]+\.[\d]+)") { - $IncompatibleToolkit = $Matches[1] + if ($NvccOut -match "release\s+(\d+)\.(\d+)") { + $tkMaj = [int]$Matches[1]; $tkMin = [int]$Matches[2] + if ($tkMaj -le $drMajorCuda) { + $NvccPath = $AnyNvcc + substep "found compatible CUDA Toolkit (nvcc: $NvccPath)" + } else { + $IncompatibleToolkit = "$tkMaj.$tkMin" + } } } } @@ -1155,26 +1166,18 @@ if ($DriverMaxCuda) { $NvccPath = Find-Nvcc } -# -- If incompatible toolkit is blocking, tell user to uninstall it -- +# A newer-major toolkit blocked by the driver: explain the mismatch. if (-not $NvccPath -and $IncompatibleToolkit) { + Write-CudaDriverToolkitMismatch -ToolkitVersion $IncompatibleToolkit -DriverMaxCuda $DriverMaxCuda if (-not $RequireOrExit) { - substep "CUDA Toolkit $IncompatibleToolkit exceeds driver max $DriverMaxCuda -- skipping; prebuilt llama.cpp needs no local toolkit" "Yellow" $script:CudaToolkitReady = $false return } + # Reached only by a source build (forced, or after a prebuilt-install failure); + # with no compatible toolkit it must fail (setup.sh degrades to CPU instead). Write-Host "" -ForegroundColor Red Write-Host "========================================================================" -ForegroundColor Red - Write-Host "[ERROR] CUDA Toolkit $IncompatibleToolkit is installed but INCOMPATIBLE" -ForegroundColor Red - Write-Host " with your NVIDIA driver (which supports up to CUDA $DriverMaxCuda)." -ForegroundColor Red - Write-Host "" -ForegroundColor Red - Write-Host " This will cause 'failed to initialize CUDA' errors at runtime." -ForegroundColor Red - Write-Host "" -ForegroundColor Red - Write-Host " To fix:" -ForegroundColor Yellow - Write-Host " 1. Open Control Panel -> Programs -> Uninstall a program" -ForegroundColor Yellow - Write-Host " 2. Uninstall 'NVIDIA CUDA Toolkit $IncompatibleToolkit'" -ForegroundColor Yellow - Write-Host " 3. Re-run setup.bat (it will install CUDA $DriverMaxCuda automatically)" -ForegroundColor Yellow - Write-Host "" -ForegroundColor Yellow - Write-Host " Alternatively, update your NVIDIA driver to one that supports CUDA $IncompatibleToolkit." -ForegroundColor Gray + Write-Host "[ERROR] CUDA source build cannot use the installed toolkit with this driver." -ForegroundColor Red Write-Host "========================================================================" -ForegroundColor Red exit 1 } @@ -1187,7 +1190,6 @@ if (-not $NvccPath -and $RequireOrExit) { if ($DriverMaxCuda) { # Query winget for available CUDA Toolkit versions $drMajor = [int]$DriverMaxCuda.Split('.')[0] - $drMinor = [int]$DriverMaxCuda.Split('.')[1] $AvailableVersions = @() try { $rawOutput = winget show Nvidia.CUDA --versions --accept-source-agreements 2>&1 | Out-String @@ -1200,13 +1202,12 @@ if (-not $NvccPath -and $RequireOrExit) { } } catch {} - # Filter to compatible versions (<= driver max) and pick the highest + # Filter to compatible major versions and pick the highest $BestVersion = $null foreach ($ver in $AvailableVersions) { $parts = $ver.Split('.') $vMajor = [int]$parts[0] - $vMinor = [int]$parts[1] - if ($vMajor -lt $drMajor -or ($vMajor -eq $drMajor -and $vMinor -le $drMinor)) { + if ($vMajor -le $drMajor) { $BestVersion = $ver break # list is descending, first match is highest compatible } @@ -1224,7 +1225,7 @@ if (-not $NvccPath -and $RequireOrExit) { substep "CUDA Toolkit $BestVersion installed (nvcc: $NvccPath)" } } else { - substep "no compatible CUDA Toolkit version found in winget (need <= $DriverMaxCuda)" "Yellow" + substep "no compatible CUDA Toolkit version found in winget (need CUDA major <= $drMajor)" "Yellow" } } else { substep "Installing CUDA Toolkit (latest) via winget..." @@ -1246,7 +1247,7 @@ if (-not $NvccPath) { } Write-Host "[ERROR] CUDA Toolkit (nvcc) is required but could not be found or installed." -ForegroundColor Red if ($DriverMaxCuda) { - Write-Host " Install CUDA Toolkit $DriverMaxCuda from https://developer.nvidia.com/cuda-toolkit-archive" -ForegroundColor Yellow + Write-Host " Install a CUDA Toolkit with major version $($DriverMaxCuda.Split('.')[0]) from https://developer.nvidia.com/cuda-toolkit-archive" -ForegroundColor Yellow } else { Write-Host " Install CUDA Toolkit from https://developer.nvidia.com/cuda-downloads" -ForegroundColor Yellow } diff --git a/studio/setup.sh b/studio/setup.sh index 9b29def859..0db9a612c8 100755 --- a/studio/setup.sh +++ b/studio/setup.sh @@ -59,8 +59,9 @@ fi # ── Output helpers ── # Consistent column layout: 2-space indent, 15-char label (fits llama-quantize), then value. # Usage: step