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.

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.

Appendix: 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)