headroomlabs-ai/headroom · error · ValueError

max_batch_units must be positive

Error message

max_batch_units must be positive

What it means

Argument-validation ValueError from the batch-grouping function: max_batch_units <= 0. max_batch_units caps how many compression units may share one batch; a zero/negative cap is nonsensical (every flush would violate it), so it is rejected immediately.

Source

Thrown at headroom/transforms/compression_batches.py:80

    *,
    min_batch_bytes: int,
    max_batch_bytes: int = DEFAULT_MAX_BATCH_BYTES,
    max_batch_units: int = DEFAULT_MAX_BATCH_UNITS,
) -> tuple[list[CompressionBatch], list[CompressionBatchEntry]]:
    """Greedily group compatible small units and skip under-floor tails.

    Callers retain the skipped entries as normal ``size_floor`` results. The
    function deliberately does not turn a unit larger than the configured
    batch ceiling into a singleton batch; those units belong to the existing
    independent compression path.
    """

    if min_batch_bytes <= 0:
        raise ValueError("min_batch_bytes must be positive")
    if max_batch_bytes < min_batch_bytes:
        raise ValueError("max_batch_bytes must be at least min_batch_bytes")
    if max_batch_units <= 0:
        raise ValueError("max_batch_units must be positive")

    batches: list[CompressionBatch] = []
    skipped: list[CompressionBatchEntry] = []
    pending: list[CompressionBatchEntry] = []
    pending_bytes = 0
    pending_key: tuple[object, ...] | None = None

    def flush() -> None:
        nonlocal pending, pending_bytes, pending_key
        if not pending:
            return
        if pending_bytes >= min_batch_bytes:
            batches.append(CompressionBatch(entries=tuple(pending), text_bytes=pending_bytes))
        else:
            skipped.extend(pending)
        pending = []
        pending_bytes = 0
        pending_key = None

View on GitHub (pinned to 322425c43b)

Solutions

  1. Set max_batch_units to a positive integer (e.g. 64).
  2. If the intent was 'no cap', pass a very large sentinel (e.g. sys.maxsize) rather than 0.
  3. Validate all three batch knobs together at config load time.

Example fix

# before
group_batches(entries, max_batch_units=0)

# after
group_batches(entries, max_batch_units=64)  # or sys.maxsize for effectively-no-cap
Defensive patterns

Strategy: validation

Validate before calling

if max_batch_units <= 0:
    raise ConfigError("max_batch_units must be > 0")
result = group_batches(entries, max_batch_units=max_batch_units)

Try / catch

try:
    group_batches(entries, max_batch_units=n)
except ValueError as e:
    if "max_batch_units" in str(e):
        group_batches(entries, max_batch_units=64)
    else:
        raise

Prevention

When it happens

Trigger: Calling the function with max_batch_units=0 or negative — commonly a config field defaulted to 0 or a subtraction that produced 0.

Common situations: New config key added with a 0 default; a 'disable unit cap' intent encoded as 0; unit tests passing literal 0 while probing boundary behavior.

Related errors


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