redis/redis-py · warning · ValueError

Item not found

Error message

Item not found

What it means

Raised as ValueError by WeightedList.remove (redis/data_structure.py:39) when the item being removed is not present in the list. WeightedList is the thread-safe priority list used internally by the Active-Active multidb client (redis/multidb/) to rank databases by weight and drive failover. remove() returns silently only if it finds the item; otherwise it raises.

Source

Thrown at redis/data_structure.py:39

            # Find insertion point using binary search
            left, right = 0, len(self._items)
            while left < right:
                mid = (left + right) // 2
                if self._items[mid][1] < weight:
                    right = mid
                else:
                    left = mid + 1

            self._items.insert(left, (item, weight))

    def remove(self, item):
        """Remove first occurrence of item"""
        with self._lock:
            for i, (stored_item, weight) in enumerate(self._items):
                if stored_item == item:
                    self._items.pop(i)
                    return weight
            raise ValueError("Item not found")

    def get_by_weight_range(
        self, min_weight: float, max_weight: float
    ) -> List[tuple[Any, Number]]:
        """Get all items within weight range"""
        with self._lock:
            result = []
            for item, weight in self._items:
                if min_weight <= weight <= max_weight:
                    result.append((item, weight))
            return result

    def get_top_n(self, n: int) -> List[tuple[Any, Number]]:
        """Get top N the highest weighted items"""
        with self._lock:
            return [(item, weight) for item, weight in self._items[:n]]

    def update_weight(self, item, new_weight: float):

View on GitHub (pinned to da03cdc7e8)

Solutions

  1. Guard the removal: iterate or snapshot the list to confirm the item exists before calling remove().
  2. Wrap remove() in try/except ValueError and treat 'not found' as a no-op if double-removal is acceptable in your flow.
  3. For the multidb client, remove each database exactly once and avoid mutating the configuration concurrently.
  4. Track add/remove symmetry in your own code so every remove() has a matching prior add().

Example fix

// before
databases.remove((db, weight))  # ValueError if not present

// after
try:
    databases.remove((db, weight))
except ValueError:
    pass  # already removed, nothing to do
Defensive patterns

Strategy: validation

Validate before calling

from redis.data_structure import WeightedList
wl = WeightedList()
wl.add(db, 1.0)
# WeightedList has no __contains__; snapshot before removing
present = any(item == db for item, _ in wl)
if present:
    wl.remove(db)

Type guard

from redis.data_structure import WeightedList

def safe_remove(wl: WeightedList, item) -> bool:
    for stored, _ in wl:
        if stored == item:
            wl.remove(item)
            return True
    return False

Try / catch

try:
    wl.remove(item)
except ValueError:
    pass  # already absent; treat as no-op

Prevention

When it happens

Trigger: Calling WeightedList.remove(item) (directly, or via the multidb client removing a database that was never added or was already removed) for an item whose value is not currently stored.

Common situations: Removing a database from the multidb client twice; removing a database whose weight tuple changed identity so equality no longer matches; concurrent remove of the same entry racing in two threads (the second finds nothing).

Related errors


AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04). Data as JSON: /data/errors/76b26301f4b0ca8f.json. Report an issue: GitHub.