{"record":{"id":"66cff8bc6bb681d4","repo":"headroomlabs-ai/headroom","slug":"target-ratio-list-length-len-target-ratio-does","errorCode":null,"errorMessage":"target_ratio list length {len(target_ratio)} does not match contents length {n}","messagePattern":"target_ratio list length (.+?) does not match contents length (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"headroom/transforms/kompress_compressor.py","lineNumber":1850,"sourceCode":"        Notes:\n            On the batched GPU path, scoring uses ``get_scores`` uniformly\n            (threshold at 0.5 when ``target_ratio`` is ``None``). This\n            matches the ONNX non-batched behavior exactly. The PyTorch\n            non-batched path applies an additional borderline + span-boost\n            rule, so results may differ by a small fraction of tokens on\n            ``target_ratio=None`` calls via the batched path vs direct\n            :meth:`compress` on PyTorch. Call :meth:`compress` directly if\n            the exact PyTorch borderline behavior is required.\n        \"\"\"\n        n = len(contents)\n        if n == 0:\n            return []\n        t_deadline = time.perf_counter() if _deadline_started_at is None else _deadline_started_at\n\n        # Normalize target_ratio to a per-text list\n        if isinstance(target_ratio, list):\n            if len(target_ratio) != n:\n                raise ValueError(\n                    f\"target_ratio list length {len(target_ratio)} does not match \"\n                    f\"contents length {n}\"\n                )\n            ratios: list[float | None] = list(target_ratio)\n        else:\n            ratios = [target_ratio] * n\n\n        # Normalize ccr_originals to a per-text list (CCR stores these instead of\n        # the possibly tag-protected ``contents`` entries; see ``compress``).\n        if ccr_originals is not None:\n            if len(ccr_originals) != n:\n                raise ValueError(\n                    f\"ccr_originals list length {len(ccr_originals)} does not match \"\n                    f\"contents length {n}\"\n                )\n            ccr_sources: list[str | None] = list(ccr_originals)\n        else:\n            ccr_sources = [None] * n","sourceCodeStart":1832,"sourceCodeEnd":1868,"githubUrl":"https://github.com/headroomlabs-ai/headroom/blob/322425c43bffde1ed0b64fecf3cf5951565dd82b/headroom/transforms/kompress_compressor.py#L1832-L1868","documentation":"Raised by the batched Kompress compression path when an explicitly passed `target_ratio` list does not have exactly one entry per item in `contents`. The API accepts either a single ratio applied to all texts or a per-text list; the length check enforces that 1:1 correspondence before any work is dispatched. It is a pure caller-side argument validation error.","triggerScenarios":"Calling `compress_batch(contents, target_ratio=[0.5, 0.3])` (or the batched `compress` overload) with a list whose length differs from `len(contents)` — e.g. 3 contents but 2 ratios, or reusing a ratios list computed for a previous, different-sized batch.","commonSituations":"Building the ratio list in a loop with an off-by-one, filtering `contents` (e.g. dropping empty strings) without filtering the parallel ratio list, or passing a shared ratio list across batches of varying size instead of the scalar form.","solutions":["Make `len(target_ratio)` equal `len(contents)` — build both lists from the same iteration/filter so they cannot diverge","If every text should get the same ratio, pass a scalar (`target_ratio=0.5`) instead of a list — the code replicates it across the batch","If ratios were computed per-text earlier, recompute or re-index them against the current `contents` (e.g. zip contents with their metadata before calling)"],"exampleFix":"# before\nratios = [0.5] * 2\nresults = kompress.compress_batch([a, b, c], target_ratio=ratios)\n\n# after\nresults = kompress.compress_batch([a, b, c], target_ratio=0.5)\n# or: ratios = [0.5] * len(contents)","handlingStrategy":"validation","validationCode":"def check_batch_args(contents: list[str], target_ratio) -> None:\n    if isinstance(target_ratio, list) and len(target_ratio) != len(contents):\n        raise ValueError(\n            f\"target_ratio has {len(target_ratio)} entries for \"\n            f\"{len(contents)} contents\"\n        )\n\ncheck_batch_args(contents, target_ratio)\nresults = kompress.compress_batch(contents, target_ratio=target_ratio)","typeGuard":"def is_aligned_ratio_list(contents: list[str], tr) -> bool:\n    return not isinstance(tr, list) or len(tr) == len(contents)","tryCatchPattern":"try:\n    results = kompress.compress_batch(contents, target_ratio=ratios)\nexcept ValueError as e:\n    if \"target_ratio list length\" in str(e):\n        ratios = [base_ratio] * len(contents)  # or rebuild per-text ratios\n        results = kompress.compress_batch(contents, target_ratio=ratios)\n    else:\n        raise","preventionTips":["Never build contents and ratios in separate passes; derive them from one zip over your source records","Prefer the scalar target_ratio when all texts share one ratio","Assert len equality in a wrapper function used by all call sites"],"tags":["validation","batch-api","kompress","precondition"],"backgroundTag":null,"analyzedSha":"322425c43bffde1ed0b64fecf3cf5951565dd82b","analyzedAt":"2026-08-15T01:03:05.481Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}