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
- 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.
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
- 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
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
- `context_window` must be a positive number of tokens, got {c
- `min_keep_steps` must be at least 0, got {min_keep_steps}.
- `min_keep_steps` must be at least 0, got {min_keep_steps}.
- `approximate_summary_tokens` must be a positive number of to
- `min_keep_steps` must be at least 1, got {min_keep_steps}. T
AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30).
Data as JSON: /api/errors/c20c609c5c666114.
Report an issue: GitHub.