python/cpython · error · ValueError

BoundedSemaphore released too many times

Error message

BoundedSemaphore released too many times

What it means

asyncio.BoundedSemaphore exists precisely to catch over-release: if release() would push the internal counter above the initial bound, it raises ValueError instead of silently growing. This surfaces unbalanced acquire/release logic that a plain Semaphore would hide.

Source

Thrown at Lib/asyncio/locks.py:461

                # `fut` is now `done()` and not `cancelled()`.
                return True
        return False


class BoundedSemaphore(Semaphore):
    """A bounded semaphore implementation.

    This raises ValueError in release() if it would increase the value
    above the initial value.
    """

    def __init__(self, value=1):
        self._bound_value = value
        super().__init__(value)

    def release(self):
        if self._value >= self._bound_value:
            raise ValueError('BoundedSemaphore released too many times')
        super().release()



class _BarrierState(enum.Enum):
    FILLING = 'filling'
    DRAINING = 'draining'
    RESETTING = 'resetting'
    BROKEN = 'broken'


class Barrier(mixins._LoopBoundMixin):
    """Asyncio equivalent to threading.Barrier

    Implements a Barrier primitive.
    Useful for synchronizing a fixed number of tasks at known synchronization
    points. Tasks block on 'wait()' and are simultaneously awoken once they
    have all made their call.

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Remove duplicate releases; prefer 'async with sem:' which releases exactly once
  2. Release only on the success path of acquire: pair acquire/release in try/finally entered after acquire completes
  3. Audit with BoundedSemaphore during development to find the imbalance, even if plain Semaphore is used in production

Example fix

# before
async with sem:
    ...
finally:
    sem.release()  # second release -> ValueError

# after
async with sem:  # releases exactly once
    ...
Defensive patterns

Strategy: validation

Validate before calling

# BoundedSemaphore exposes _bound_value; check headroom before release
if sem._value >= sem._bound_value:
    # release() would raise; skip or fix imbalance

Try / catch

try:
    sem.release()
except ValueError:
    # over-release bug surfaced; log and fix acquire/release pairing

Prevention

When it happens

Trigger: Calling release() more times than acquire() succeeded; releasing in a finally block of a code path that never acquired; mixing manual release with 'async with sem' (which releases automatically).

Common situations: 'async with sem:' plus a manual sem.release() in cleanup; exception paths that release on tasks which timed out before acquiring; refactors that moved acquire() behind a condition but kept the unconditional release.

Related errors


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