deepset-ai/haystack · error

`compact_at` and `compact_to` must satisfy 0 < compact_to <

Error message

`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.

What it means

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.

Source

Thrown at haystack/hooks/compaction/hooks.py:94

        """
        Initialize the hook with a compactor and the window it has to fit in.

        :param compactor: The `Compactor` that rewrites the conversation.
        :param context_window: The model's context window in tokens. Everything else is a fraction of this, so moving to
            a different model means changing only this number.
        :param compact_at: The fraction of the window at which compaction starts. Leave room above it for the reply and
            the tool results it triggers, which land on top of what was measured.
        :param compact_to: The fraction of the window compaction aims to bring the conversation down to. Lower means
            compacting less often but losing more each time.
        :param token_counter: The `TokenCounter` used to size the messages the chat generator has not reported on yet.
            Defaults to `ApproximateTokenCounter`, which needs no extra dependency.
        :raises ValueError: If `context_window` is not positive, or the fractions are not
            `0 < compact_to < compact_at <= 1`.
        """
        if context_window < 1:
            raise ValueError(f"`context_window` must be a positive number of tokens, got {context_window}.")
        if not 0 < compact_to < compact_at <= 1:
            raise ValueError(
                f"`compact_at` and `compact_to` must satisfy 0 < compact_to < compact_at <= 1, got "
                f"compact_at={compact_at} and compact_to={compact_to}. A target at or above the trigger would leave "
                f"the conversation over the trigger after compacting, so it would be attempted again every step."
            )
        self.compactor = compactor
        self.context_window = context_window
        self.compact_at = compact_at
        self.compact_to = compact_to
        self.token_counter = token_counter or ApproximateTokenCounter()

    def run(self, state: State) -> None:
        """
        Compact `state.data["messages"]` if the conversation fills too much of the window.

        :param state: The Agent's live `State`. Read to decide whether to compact, and rewritten in place when the
            compactor returns a compacted conversation.
        :returns: None. The hook mutates `state` in place.
        """

View on GitHub (pinned to e318778c9b)

Solutions

  1. Order the values so compact_to < compact_at, e.g. compact_at=0.9, compact_to=0.7.
  2. Ensure both are fractions in (0, 1], not percentages or ints.
  3. Validate config-derived values before construction: assert 0 < to < at <= 1.

Example fix

// before
hook = CompactionHook(compact_at=0.7, compact_to=0.9)
// after
hook = CompactionHook(compact_at=0.9, compact_to=0.7)
Defensive patterns

Strategy: validation

Validate before calling

def validate_thresholds(compact_at, compact_to):
    if not (0 < compact_to < compact_at <= 1):
        raise ValueError(f"need 0 < compact_to < compact_at <= 1, got at={compact_at}, to={compact_to}")
validate_thresholds(0.9, 0.7)

Type guard

def valid_fractions(at, to) -> bool:
    return 0 < to < at <= 1

Try / catch

try:
    hook = CompactionHook(compact_at=at, compact_to=to)
except ValueError as e:
    logger.error("bad thresholds: %s", e)
    hook = CompactionHook(compact_at=0.9, compact_to=0.7)

Prevention

When it happens

Trigger: 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.

Common situations: 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).

Related errors


AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30). Data as JSON: /api/errors/c20c609c5c666114. Report an issue: GitHub.