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 = None

View on GitHub (pinned to e84aa99b32)

Solutions

  1. Pass an integer >= 1: GPURunner(num_gpus=1).
  2. Validate the computed GPU count before construction and fall back to CPU execution when it is 0.
  3. Check that the machine actually has GPUs and drivers are installed so detection returns a positive count.
  4. 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

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


AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08). Data as JSON: /api/errors/7b2b064eb5c75833. Report an issue: GitHub.