{"record":{"id":"c20c609c5c666114","repo":"deepset-ai/haystack","slug":"compact-at-and-compact-to-must-satisfy-0-com","errorCode":null,"errorMessage":"`compact_at` and `compact_to` must satisfy 0 < compact_to < compact_at <= 1, got compact_at={compact_at} and compact_to={compact_to}. A target at or above the trigger would leave the conversation over the trigger after compacting, so it would be attempted again every step.","messagePattern":"`compact_at` and `compact_to` must satisfy 0 < compact_to < compact_at <= 1, got compact_at=(.+?) and compact_to=(.+?)\\. A target at or above the trigger would leave the conversation over the trigger after compacting, so it would be attempted again every step\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"haystack/hooks/compaction/hooks.py","lineNumber":94,"sourceCode":"        \"\"\"\n        Initialize the hook with a compactor and the window it has to fit in.\n\n        :param compactor: The `Compactor` that rewrites the conversation.\n        :param context_window: The model's context window in tokens. Everything else is a fraction of this, so moving to\n            a different model means changing only this number.\n        :param compact_at: The fraction of the window at which compaction starts. Leave room above it for the reply and\n            the tool results it triggers, which land on top of what was measured.\n        :param compact_to: The fraction of the window compaction aims to bring the conversation down to. Lower means\n            compacting less often but losing more each time.\n        :param token_counter: The `TokenCounter` used to size the messages the chat generator has not reported on yet.\n            Defaults to `ApproximateTokenCounter`, which needs no extra dependency.\n        :raises ValueError: If `context_window` is not positive, or the fractions are not\n            `0 < compact_to < compact_at <= 1`.\n        \"\"\"\n        if context_window < 1:\n            raise ValueError(f\"`context_window` must be a positive number of tokens, got {context_window}.\")\n        if not 0 < compact_to < compact_at <= 1:\n            raise ValueError(\n                f\"`compact_at` and `compact_to` must satisfy 0 < compact_to < compact_at <= 1, got \"\n                f\"compact_at={compact_at} and compact_to={compact_to}. A target at or above the trigger would leave \"\n                f\"the conversation over the trigger after compacting, so it would be attempted again every step.\"\n            )\n        self.compactor = compactor\n        self.context_window = context_window\n        self.compact_at = compact_at\n        self.compact_to = compact_to\n        self.token_counter = token_counter or ApproximateTokenCounter()\n\n    def run(self, state: State) -> None:\n        \"\"\"\n        Compact `state.data[\"messages\"]` if the conversation fills too much of the window.\n\n        :param state: The Agent's live `State`. Read to decide whether to compact, and rewritten in place when the\n            compactor returns a compacted conversation.\n        :returns: None. The hook mutates `state` in place.\n        \"\"\"","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/deepset-ai/haystack/blob/e318778c9bf60a1963e3b5f451359655dd696c30/haystack/hooks/compaction/hooks.py#L76-L112","documentation":"This ValueError fires when the compaction hook's thresholds are not ordered as 0 < compact_to < compact_at <= 1 (haystack/hooks/compaction/hooks.py:94). compact_at is the token-fraction trigger and compact_to the target fraction; if the target is at or above the trigger, compaction would never get the conversation under the trigger and would re-fire every step.","triggerScenarios":"Constructing the hook with compact_to >= compact_at, either value <= 0, or compact_at > 1 — e.g. compact_at=0.5, compact_to=0.7, or compact_at=1.5.","commonSituations":"Swapping the two keyword arguments by mistake; tuning thresholds and accidentally making the target higher than the trigger; passing percentages (70) instead of fractions (0.7).","solutions":["Order the values so compact_to < compact_at, e.g. compact_at=0.9, compact_to=0.7.","Ensure both are fractions in (0, 1], not percentages or ints.","Validate config-derived values before construction: assert 0 < to < at <= 1."],"exampleFix":"// before\nhook = CompactionHook(compact_at=0.7, compact_to=0.9)\n// after\nhook = CompactionHook(compact_at=0.9, compact_to=0.7)","handlingStrategy":"validation","validationCode":"def validate_thresholds(compact_at, compact_to):\n    if not (0 < compact_to < compact_at <= 1):\n        raise ValueError(f\"need 0 < compact_to < compact_at <= 1, got at={compact_at}, to={compact_to}\")\nvalidate_thresholds(0.9, 0.7)","typeGuard":"def valid_fractions(at, to) -> bool:\n    return 0 < to < at <= 1","tryCatchPattern":"try:\n    hook = CompactionHook(compact_at=at, compact_to=to)\nexcept ValueError as e:\n    logger.error(\"bad thresholds: %s\", e)\n    hook = CompactionHook(compact_at=0.9, compact_to=0.7)","preventionTips":["Keep compact_to ~0.2 below compact_at for headroom","Use fractions (0-1), never percentages","Validate before constructing when values come from config","Unit-test threshold combinations in CI"],"tags":["validation","constructor","config","compaction"],"backgroundTag":"invalid-constructor-argument","analyzedSha":"e318778c9bf60a1963e3b5f451359655dd696c30","analyzedAt":"2026-08-30T11:45:20.711Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}