headroomlabs-ai/headroom · error · ValueError

max_size must be at least 1, got {max_size}

Error message

max_size must be at least 1, got {max_size}

What it means

LRUMemoryCache.__init__ validates its max_size argument and raises ValueError when it is less than 1, because an empty cache can never store anything and negative sizes are meaningless. It is a fail-fast config check before any OrderedDict/lock state is created.

Source

Thrown at headroom/memory/adapters/cache.py:54

    The cache uses an OrderedDict internally where:
    - Most recently used items are at the end
    - Least recently used items are at the beginning
    - On capacity overflow, the first (oldest) item is evicted
    """

    def __init__(self, max_size: int = 1000) -> None:
        """Initialize the LRU cache.

        Args:
            max_size: Maximum number of entries to store. When exceeded,
                      the least recently used entry is evicted.

        Raises:
            ValueError: If max_size is less than 1.
        """
        if max_size < 1:
            raise ValueError(f"max_size must be at least 1, got {max_size}")

        self._max_size = max_size
        self._cache: OrderedDict[str, Memory] = OrderedDict()
        self._lock = Lock()

    async def get(self, memory_id: str) -> Memory | None:
        """Get a memory from the cache.

        Moves the accessed item to the end (most recently used position).

        Args:
            memory_id: The memory ID to retrieve.

        Returns:
            The Memory object if found, None otherwise.
        """
        with self._lock:
            if memory_id not in self._cache:

View on GitHub (pinned to 322425c43b)

Solutions

  1. Pass max_size >= 1: LRUMemoryCache(max_size=1) is the smallest valid cache.
  2. If the intent was 'no caching', skip constructing the cache rather than passing 0.
  3. Guard derived values: max(max_size, 1) at the call site when the number is computed.
  4. Validate config values at load time with a clear message before they reach the constructor.

Example fix

# before
cache = LRUMemoryCache(max_size=0)  # ValueError

# after
cache = LRUMemoryCache(max_size=1)  # smallest valid; or don't cache at all
Defensive patterns

Strategy: validation

Validate before calling

def resolve_cache_size(raw: int | str | None, default: int = 1000) -> int:
    n = int(raw) if raw is not None else default
    if n < 1:
        raise ValueError(f"cache size must be >= 1, got {n}; unset means 'no cache', 0 is invalid")
    return n

Type guard

def is_valid_cache_size(n: object) -> bool:
    """LRUMemoryCache accepts max_size only when it's an int >= 1."""
    return isinstance(n, int) and not isinstance(n, bool) and n >= 1

Try / catch

try:
    cache = LRUMemoryCache(max_size=cfg["cache_size"])
except ValueError as e:
    raise SystemExit(f"invalid cache config: {e}; set cache_size >= 1 or disable caching") from None

Prevention

When it happens

Trigger: Constructing LRUMemoryCache(max_size=0) or a negative value — typically from a config file field, an environment-derived value that defaulted to 0, or arithmetic like max_size=len(items)-10 evaluating to <= 0.

Common situations: YAML/TOML config with cache_size: 0 to 'disable' the cache (use None/bypass instead); env var parsed to 0 when unset; test fixtures shrinking cache size below 1; derived sizes (total_budget - overhead) going negative.

Related errors


AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15). Data as JSON: /api/errors/35f96e85541e2dca. Report an issue: GitHub.