dgtlmoon/changedetection.io · warning · LLMInputTooLargeError

Change too large for AI summary ({len(text):,} chars, limit

Error message

Change too large for AI summary ({len(text):,} chars, limit {max_chars:,})

What it means

A guard in the LLM evaluator that raises LLMInputTooLargeError before any model call when the text to summarise exceeds max_chars. This is a deliberate pre-flight check so huge diffs never reach (and cost) the LLM API.

Source

Thrown at changedetectionio/llm/evaluator.py:83

    Always returns at least 1 — unlimited is not permitted.
    """
    env_val = os.getenv('LLM_MAX_INPUT_CHARS', '').strip()
    if env_val.isdigit() and int(env_val) > 0:
        return int(env_val)
    stored = get_llm_settings(datastore).max_input_chars
    if stored and stored > 0:
        return stored
    return _DEFAULT_MAX_INPUT_CHARS


class LLMInputTooLargeError(Exception):
    pass


def _check_input_size(text: str, max_chars: int) -> None:
    """Raise LLMInputTooLargeError if text exceeds max_chars."""
    if len(text) > max_chars:
        raise LLMInputTooLargeError(
            f"Change too large for AI summary ({len(text):,} chars, limit {max_chars:,})"
        )


def _thinking_extra_body(model: str, budget: int) -> dict | None:
    """Return litellm extra_body to control thinking for models that support it.

    The `thinkingConfig.thinkingBudget` payload is Gemini-specific (Anthropic and
    OpenAI reasoning models use different parameters), so we gate on the gemini/
    provider prefix first, then defer to litellm's model registry for the actual
    "does this model think?" decision. That picks up new Gemini variants and
    rolling aliases (`gemini-flash-latest`, etc.) as litellm's registry tracks
    them, without us hardcoding model names here.
    """
    if not model.startswith('gemini/'):
        return None
    try:
        import litellm

View on GitHub (pinned to 5d9c7c6da7)

Solutions

  1. Increase max_chars in the LLM/summary settings if your model context allows it
  2. Limit what is sent: use CSS/xpath filters or ignore-text rules on the watch so the diff shrinks
  3. Catch LLMInputTooLargeError and skip AI summarisation for oversized changes

Example fix

# before
summary = summarise_change(diff, max_chars=50000)
# after
try:
    summary = summarise_change(diff, max_chars=50000)
except LLMInputTooLargeError:
    summary = None  # change too big to summarise
Defensive patterns

Strategy: type-guard

Validate before calling

if len(diff_text) > max_chars:
    diff_text = None  # skip AI summary for oversized change

Type guard

def summarisable(text: str, max_chars: int) -> bool:
    return len(text) <= max_chars

Try / catch

from changedetectionio.llm.evaluator import LLMInputTooLargeError
try:
    summary = summarise_change(diff, max_chars=mc)
except LLMInputTooLargeError:
    summary = None

Prevention

When it happens

Trigger: Calling summarise_change, preview_extract, or evaluate_change with a change/diff whose character count exceeds the configured max_chars limit (set by the LLM config, e.g. notification/summariser settings). Large page snapshots or full-page diffs easily exceed it.

Common situations: Watching pages with very large content (logs, feeds, minified JS diffs); enabling AI summaries on watches whose snapshots are megabytes; lowering the char limit in config while existing watches have big diffs.

Related errors


AI-assisted analysis of dgtlmoon/changedetection.io@5d9c7c6da7 (2026-08-27). Data as JSON: /api/errors/3281ba67d694ef11. Report an issue: GitHub.