deepset-ai/haystack · error

`max_total_tokens` must be a positive number of tokens, got

Error message

`max_total_tokens` must be a positive number of tokens, got {max_total_tokens}.

What it means

WallClockTokenBudget (haystack/hooks/budget/hooks.py) requires max_total_tokens to be a positive integer of tokens. A value less than 1 (0, negative, or otherwise falsy-as-small) would make the Agent stop immediately or behave nonsensically, so __init__ raises this ValueError with the offending value.

Source

Thrown at haystack/hooks/budget/hooks.py:58

        hooks={"before_llm": [TokenBudgetHook(max_total_tokens=100_000)]},
    )

    result = agent.run(messages=[...])
    ```
    """

    allowed_hook_points = ("before_llm",)

    def __init__(self, *, max_total_tokens: int, add_final_message: bool = False) -> None:
        """
        Create a token budget hook.

        :param max_total_tokens: Maximum cumulative token usage before the Agent is stopped.
        :param add_final_message: Whether to append an assistant message explaining why the Agent stopped.
        :raises ValueError: If `max_total_tokens` is less than 1.
        """
        if max_total_tokens < 1:
            raise ValueError(f"`max_total_tokens` must be a positive number of tokens, got {max_total_tokens}.")
        self.max_total_tokens = max_total_tokens
        self.add_final_message = add_final_message

    def run(self, state: State) -> None:
        """
        Stop the Agent if its cumulative token usage has reached the budget.

        :param state: Agent state containing the cumulative token usage.
        """
        usage = state.data.get("token_usage") or {}
        # Not every chat generator reports `total_tokens`, so fall back to summing the input and output keys across
        # the known naming conventions.
        total_tokens = _first_numeric(usage, ("total_tokens",))
        if not total_tokens:
            total_tokens = _first_numeric(usage, _INPUT_TOKEN_KEYS) + _first_numeric(usage, _OUTPUT_TOKEN_KEYS)
        if total_tokens >= self.max_total_tokens:
            logger.warning(
                "Agent reached its token budget of {max_total_tokens} ({total_tokens} used); requesting a stop.",

View on GitHub (pinned to e318778c9b)

Solutions

  1. Pass a positive integer, e.g. WallClockTokenBudget(max_total_tokens=10000).
  2. Fix the config/env lookup so the value defaults to a sensible positive number.
  3. Validate/normalize before constructing: max(1, int(configured_value)).

Example fix

// before
budget = WallClockTokenBudget(max_total_tokens=int(os.getenv('MAX_TOKENS', 0)))
// after
max_tokens = int(os.getenv('MAX_TOKENS', '10000'))
budget = WallClockTokenBudget(max_total_tokens=max(1, max_tokens))
Defensive patterns

Strategy: validation

Validate before calling

if not isinstance(max_total_tokens, int) or max_total_tokens < 1:
    raise ValueError('max_total_tokens must be a positive integer')

Type guard

def is_positive_int(v: object) -> bool:
    return isinstance(v, int) and not isinstance(v, bool) and v >= 1

Try / catch

try:
    budget = WallClockTokenBudget(max_total_tokens=cfg['max_total_tokens'])
except ValueError as e:
    if 'max_total_tokens' in str(e):
        budget = WallClockTokenBudget(max_total_tokens=10000)
    else:
        raise

Prevention

When it happens

Trigger: Instantiating WallClockTokenBudget-like budget hook with max_total_tokens=0, a negative number, or a value computed from an empty/failed config lookup (e.g. int(os.getenv('MAX_TOKENS', 0))).

Common situations: Environment variable for the budget missing so the default 0 is used; copying an example that left the placeholder at 0; a unit conversion bug producing a negative value.

Related errors


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