cocoindex-io/cocoindex · error · ValueError
num_gpus must be >= 1, got {num_gpus}
Error message
num_gpus must be >= 1, got {num_gpus} What it means
Constructor validation of the GPU-arbitration pool size. _GpuPool tracks one fractional capacity slot per GPU, and initialize with 0 or negative GPUs would leave the pool permanently unable to satisfy any acquire() request (callers would block forever). This ValueError fires when configure_gpu_pool(N) or the auto-detected pool size yields N < 1 — e.g. a mistyped COCOINDEX_NUM_GPUS environment variable. Pass an integer >= 1.
Source
Thrown at python/cocoindex/_internal/runner.py:278
"""Tracks fractional GPU capacity across multiple GPUs.
Each GPU starts with capacity 1.0. ``acquire(fraction)`` blocks until a
GPU with enough remaining capacity is available, then returns its id.
``release(gpu_id, fraction)`` restores capacity and wakes waiters.
The default pool size is auto-detected from ``COCOINDEX_NUM_GPUS``,
``CUDA_VISIBLE_DEVICES``, or ``nvidia-smi`` (falling back to 1).
Call ``configure_gpu_pool(N)`` to override programmatically.
"""
_num_gpus: int
_capacity: list[float]
_cond: asyncio.Condition | None
_bound_loop: asyncio.AbstractEventLoop | None
def __init__(self, num_gpus: int) -> None:
if num_gpus < 1:
raise ValueError(f"num_gpus must be >= 1, got {num_gpus}")
self._num_gpus = num_gpus
self._capacity = [1.0] * num_gpus
self._cond = None
self._bound_loop = None
@property
def num_gpus(self) -> int:
return self._num_gpus
def _get_cond(self) -> asyncio.Condition:
loop = asyncio.get_running_loop()
if self._cond is None or self._bound_loop is not loop:
self._cond = asyncio.Condition()
self._bound_loop = loop
return self._cond
def _find_available(self, fraction: float) -> int | None:
best_gpu = NoneView on GitHub (pinned to e84aa99b32)
Solutions
- Pass an integer >= 1: GPURunner(num_gpus=1).
- Validate the computed GPU count before construction and fall back to CPU execution when it is 0.
- Check that the machine actually has GPUs and drivers are installed so detection returns a positive count.
- Fix the environment variable or config file supplying the GPU count.
Example fix
// before
gpus = int(os.environ.get("NUM_GPUS", "0"))
runner = GPURunner(num_gpus=gpus) # ValueError
// after
num_gpus = int(os.environ.get("NUM_GPUS", "1"))
if num_gpus < 1:
raise SystemExit("No GPUs configured; set NUM_GPUS>=1 or use CPU runner")
runner = GPURunner(num_gpus=num_gpus) Defensive patterns
Strategy: validation
Validate before calling
if not isinstance(num_gpus, int) or num_gpus < 1:
raise ValueError(f"num_gpus must be >= 1, got {num_gpus}") Type guard
def valid_gpu_count(n: object) -> bool:
return isinstance(n, int) and not isinstance(n, bool) and n >= 1 Try / catch
try:
runner = GPURunner(num_gpus=num_gpus)
except ValueError:
runner = CPURunner() # or fail fast with a clear config message Prevention
- Validate GPU counts from environment/config before constructing the runner
- Use a positive default (1) rather than 0 when detection is unavailable
- Confirm GPUs exist on the host (nvidia-smi) before auto-configuring
When it happens
Trigger: Calling GPURunner(num_gpus=0), a negative value, or passing a computed value that evaluates to 0 (e.g. len(gpus) on an empty detection list).
Common situations: Auto-configuring from torch.cuda.device_count() or nvidia-smi output on a machine without GPUs, or reading an unset/zero environment variable such as NUM_GPUS=0.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- fraction must be in (0, 1.0], got {fraction}
- expected None{loc}, got {type(value).__name__}
- expected {tp}{loc}, got {type(value).__name__}: {value!r}
- expected tuple{loc}, got {type(value).__name__}
- expected {tp}{loc}, got {type(value).__name__}
AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08).
Data as JSON: /api/errors/7b2b064eb5c75833.
Report an issue: GitHub.