headroomlabs-ai/headroom · error · ValueError

max_sessions must be > 0

Error message

max_sessions must be > 0

What it means

SessionCcrTracker is a bounded LRU keyed by (provider, session_id) that records which sessions have already done context-clone reuse (CCR). Its constructor validates max_sessions > 0 because an OrderedDict-based bound of 0 or negative would evict every entry immediately or misbehave. This is a direct-constructor guard; from configuration the value normally arrives via resolve_beta_tracker_max_sessions() (see the HEADROOM_BETA_TRACKER_MAX_SESSIONS errors).

Source

Thrown at headroom/proxy/ccr_session_tracker.py:14

"""Session-scoped state for sticky CCR retrieval tool injection."""

from __future__ import annotations

import threading
from collections import OrderedDict


class SessionCcrTracker:
    """Bounded LRU tracker recording per-provider/session CCR state."""

    def __init__(self, max_sessions: int) -> None:
        if max_sessions <= 0:
            raise ValueError("max_sessions must be > 0")
        self._max_sessions = max_sessions
        self._lock = threading.RLock()
        self._sessions: OrderedDict[tuple[str, str], tuple[bool, bytes | None]] = OrderedDict()

    @property
    def active_sessions(self) -> int:
        with self._lock:
            return len(self._sessions)

    def _key(self, provider: str, session_id: str) -> tuple[str, str]:
        return (provider, session_id)

    def has_done_ccr(self, provider: str, session_id: str) -> bool:
        """Return True when this session has previously performed CCR."""

        if not provider:
            raise ValueError("provider must be non-empty")
        if not session_id:

View on GitHub (pinned to 322425c43b)

Solutions

  1. Pass a positive bound: SessionCcrTracker(max_sessions=1000).
  2. If the value is computed, clamp or validate it before construction: max(1, computed).
  3. Route user-supplied values through resolve_beta_tracker_max_sessions() so they get the same validation and default.

Example fix

# before
tracker = SessionCcrTracker(max_sessions=0)

# after
tracker = SessionCcrTracker(max_sessions=1)  # minimum valid bound
Defensive patterns

Strategy: validation

Validate before calling

def make_tracker(max_sessions: int | None) -> SessionCcrTracker:
    return SessionCcrTracker(max_sessions=1000 if not max_sessions or max_sessions <= 0 else max_sessions)

Type guard

def valid_bound(value: object) -> bool:
    return isinstance(value, int) and value > 0

Try / catch

try:
    tracker = SessionCcrTracker(max_sessions=bound)
except ValueError:
    bound = 1000
    tracker = SessionCcrTracker(max_sessions=bound)  # log the substitution

Prevention

When it happens

Trigger: Calling SessionCcrTracker(max_sessions=0) or with a negative int in code or tests — typically when computing the bound from a config value, len() of an empty collection, or a subtraction that underflows.

Common situations: Test fixtures passing 0 to keep the tracker 'off'; deriving max_sessions from another quantity (e.g. max_total - reserved) that can legitimately reach 0; wiring an env-configured value straight into the constructor without the resolver's validation.

Related errors


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