{"record":{"id":"1d39cf09703b1d28","repo":"headroomlabs-ai/headroom","slug":"min-batch-bytes-must-be-positive","errorCode":null,"errorMessage":"min_batch_bytes must be positive","messagePattern":"min_batch_bytes must be positive","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"headroom/transforms/compression_batches.py","lineNumber":76,"sourceCode":"\n\ndef build_compression_batches(\n    entries: list[CompressionBatchEntry],\n    *,\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:","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/headroomlabs-ai/headroom/blob/322425c43bffde1ed0b64fecf3cf5951565dd82b/headroom/transforms/compression_batches.py#L58-L94","documentation":"Argument-validation ValueError from build/plan-compression-batches in compression_batches.py: min_batch_bytes was <= 0. The batching function groups small compression units into shared batches and requires a positive floor before it will flush a pending batch; zero or negative floors make the greedy grouping meaningless, so it fails fast.","triggerScenarios":"Calling the batch-grouping function with min_batch_bytes=0 or a negative value — typically from a config where the field was left unset (defaulting to 0) or computed as a difference that went negative.","commonSituations":"A settings file with min_batch_bytes: 0 meaning 'no minimum' to the author; deriving the value from a percentage of a zero-sized budget; copying an example config that omitted the field.","solutions":["Set min_batch_bytes to a positive byte count (e.g. 2048).","If you intended 'no batching', disable batching at the caller level rather than passing 0.","Validate config at load time so the failure surfaces at startup, not mid-compression."],"exampleFix":"# before\nplan = group_batches(entries, min_batch_bytes=0)\n\n# after\nMIN_BATCH_BYTES = 2048\nassert MIN_BATCH_BYTES > 0\nplan = group_batches(entries, min_batch_bytes=MIN_BATCH_BYTES)","handlingStrategy":"validation","validationCode":"if min_batch_bytes <= 0:\n    raise ConfigError(\"min_batch_bytes must be > 0\")\nresult = group_batches(entries, min_batch_bytes=min_batch_bytes)","typeGuard":null,"tryCatchPattern":"try:\n    batches, skipped = group_batches(entries, min_batch_bytes=v)\nexcept ValueError as e:\n    if \"min_batch_bytes\" in str(e):\n        batches, skipped = group_batches(entries, min_batch_bytes=2048)  # safe default\n    else:\n        raise","preventionTips":["Validate all batch knobs in one config-check function at startup.","Never use 0 to mean 'disabled' — disable batching at the caller instead.","Unit-test config boundaries (0, negative, min>max)."],"tags":["validation","configuration","batching"],"backgroundTag":null,"analyzedSha":"322425c43bffde1ed0b64fecf3cf5951565dd82b","analyzedAt":"2026-08-15T01:03:05.481Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}