python/cpython · error · ValueError

Semaphore initial value must be >= 0

Error message

Semaphore initial value must be >= 0

What it means

asyncio.Semaphore.__init__ validates that the initial counter value is non-negative; a negative value would make the semaphore permanently un-acquirable and its invariant (value >= 0) meaningless, so ValueError is raised at construction.

Source

Thrown at Lib/asyncio/locks.py:366

class Semaphore(_ContextManagerMixin, mixins._LoopBoundMixin):
    """A Semaphore implementation.

    A semaphore manages an internal counter which is decremented by each
    acquire() call and incremented by each release() call.  The counter
    can never go below zero; when acquire() finds that it is zero, it
    blocks, waiting until some other thread calls release().

    Semaphores also support the context management protocol.

    The optional argument gives the initial value for the internal
    counter; it defaults to 1. If the value given is less than 0,
    ValueError is raised.
    """

    def __init__(self, value=1):
        if value < 0:
            raise ValueError("Semaphore initial value must be >= 0")
        self._waiters = None
        self._value = value

    def __repr__(self):
        res = super().__repr__()
        extra = 'locked' if self.locked() else f'unlocked, value:{self._value}'
        if self._waiters:
            extra = f'{extra}, waiters:{len(self._waiters)}'
        return f'<{res[1:-1]} [{extra}]>'

    def locked(self):
        """Returns True if semaphore cannot be acquired immediately."""
        # Due to state, or FIFO rules (must allow others to run first).
        return self._value == 0 or (
            any(not w.cancelled() for w in (self._waiters or ())))

    async def acquire(self):
        """Acquire a semaphore.

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Clamp at the boundary: max(0, value)
  2. Validate configuration before constructing sync primitives; fail fast with a clear config error
  3. Treat 0 explicitly if 'block everything' is intended (0 is legal)

Example fix

# before
sem = asyncio.Semaphore(cfg.max_concurrency - cfg.reserved)  # can be < 0

# after
value = max(0, cfg.max_concurrency - cfg.reserved)
sem = asyncio.Semaphore(value)
Defensive patterns

Strategy: validation

Validate before calling

value = int(value)
if value < 0:
    raise ValueError(f'concurrency must be >= 0, got {value}')
sem = asyncio.Semaphore(value)

Type guard

def is_valid_semaphore_value(v) -> bool:
    return isinstance(v, int) and v >= 0

Prevention

When it happens

Trigger: asyncio.Semaphore(-1) directly; computing the value from configuration/limits that can go negative (e.g. capacity - load); Semaphore(value=len(pool) - workers) with an empty pool.

Common situations: Config-derived concurrency limits; YAML/ENV-provided values parsed without bounds checking; arithmetic on sizes that underflow to negative on small deployments.

Related errors


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/74eee506d7d23138. Report an issue: GitHub.