{"record":{"id":"a2453d20f35ffa88","repo":"headroomlabs-ai/headroom","slug":"max-batch-units-must-be-positive","errorCode":null,"errorMessage":"max_batch_units must be positive","messagePattern":"max_batch_units must be positive","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"headroom/transforms/compression_batches.py","lineNumber":80,"sourceCode":"    *,\n    min_batch_bytes: int,\n    max_batch_bytes: int = DEFAULT_MAX_BATCH_BYTES,\n    max_batch_units: int = DEFAULT_MAX_BATCH_UNITS,\n) -> tuple[list[CompressionBatch], list[CompressionBatchEntry]]:\n    \"\"\"Greedily group compatible small units and skip under-floor tails.\n\n    Callers retain the skipped entries as normal ``size_floor`` results. The\n    function deliberately does not turn a unit larger than the configured\n    batch ceiling into a singleton batch; those units belong to the existing\n    independent compression path.\n    \"\"\"\n\n    if min_batch_bytes <= 0:\n        raise ValueError(\"min_batch_bytes must be positive\")\n    if max_batch_bytes < min_batch_bytes:\n        raise ValueError(\"max_batch_bytes must be at least min_batch_bytes\")\n    if max_batch_units <= 0:\n        raise ValueError(\"max_batch_units must be positive\")\n\n    batches: list[CompressionBatch] = []\n    skipped: list[CompressionBatchEntry] = []\n    pending: list[CompressionBatchEntry] = []\n    pending_bytes = 0\n    pending_key: tuple[object, ...] | None = None\n\n    def flush() -> None:\n        nonlocal pending, pending_bytes, pending_key\n        if not pending:\n            return\n        if pending_bytes >= min_batch_bytes:\n            batches.append(CompressionBatch(entries=tuple(pending), text_bytes=pending_bytes))\n        else:\n            skipped.extend(pending)\n        pending = []\n        pending_bytes = 0\n        pending_key = None","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/headroomlabs-ai/headroom/blob/322425c43bffde1ed0b64fecf3cf5951565dd82b/headroom/transforms/compression_batches.py#L62-L98","documentation":"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.","triggerScenarios":"Calling the function with max_batch_units=0 or negative — commonly a config field defaulted to 0 or a subtraction that produced 0.","commonSituations":"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.","solutions":["Set max_batch_units to a positive integer (e.g. 64).","If the intent was 'no cap', pass a very large sentinel (e.g. sys.maxsize) rather than 0.","Validate all three batch knobs together at config load time."],"exampleFix":"# before\ngroup_batches(entries, max_batch_units=0)\n\n# after\ngroup_batches(entries, max_batch_units=64)  # or sys.maxsize for effectively-no-cap","handlingStrategy":"validation","validationCode":"if max_batch_units <= 0:\n    raise ConfigError(\"max_batch_units must be > 0\")\nresult = group_batches(entries, max_batch_units=max_batch_units)","typeGuard":null,"tryCatchPattern":"try:\n    group_batches(entries, max_batch_units=n)\nexcept ValueError as e:\n    if \"max_batch_units\" in str(e):\n        group_batches(entries, max_batch_units=64)\n    else:\n        raise","preventionTips":["Use a positive default; use sys.maxsize for 'no cap', never 0.","Validate the whole batch config in one place.","Add config-schema constraints (minimum: 1) when settings come from YAML/TOML."],"tags":["validation","configuration","batching"],"backgroundTag":null,"analyzedSha":"322425c43bffde1ed0b64fecf3cf5951565dd82b","analyzedAt":"2026-08-15T01:03:05.481Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}