{"record":{"id":"3281ba67d694ef11","repo":"dgtlmoon/changedetection.io","slug":"change-too-large-for-ai-summary-len-text-cha","errorCode":null,"errorMessage":"Change too large for AI summary ({len(text):,} chars, limit {max_chars:,})","messagePattern":"Change too large for AI summary \\((.+?) chars, limit (.+?)\\)","errorType":"exception","errorClass":"LLMInputTooLargeError","httpStatus":null,"severity":"warning","filePath":"changedetectionio/llm/evaluator.py","lineNumber":83,"sourceCode":"    Always returns at least 1 — unlimited is not permitted.\n    \"\"\"\n    env_val = os.getenv('LLM_MAX_INPUT_CHARS', '').strip()\n    if env_val.isdigit() and int(env_val) > 0:\n        return int(env_val)\n    stored = get_llm_settings(datastore).max_input_chars\n    if stored and stored > 0:\n        return stored\n    return _DEFAULT_MAX_INPUT_CHARS\n\n\nclass LLMInputTooLargeError(Exception):\n    pass\n\n\ndef _check_input_size(text: str, max_chars: int) -> None:\n    \"\"\"Raise LLMInputTooLargeError if text exceeds max_chars.\"\"\"\n    if len(text) > max_chars:\n        raise LLMInputTooLargeError(\n            f\"Change too large for AI summary ({len(text):,} chars, limit {max_chars:,})\"\n        )\n\n\ndef _thinking_extra_body(model: str, budget: int) -> dict | None:\n    \"\"\"Return litellm extra_body to control thinking for models that support it.\n\n    The `thinkingConfig.thinkingBudget` payload is Gemini-specific (Anthropic and\n    OpenAI reasoning models use different parameters), so we gate on the gemini/\n    provider prefix first, then defer to litellm's model registry for the actual\n    \"does this model think?\" decision. That picks up new Gemini variants and\n    rolling aliases (`gemini-flash-latest`, etc.) as litellm's registry tracks\n    them, without us hardcoding model names here.\n    \"\"\"\n    if not model.startswith('gemini/'):\n        return None\n    try:\n        import litellm","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/dgtlmoon/changedetection.io/blob/5d9c7c6da76340597243e8163c4f2439237fa0e8/changedetectionio/llm/evaluator.py#L65-L101","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Increase max_chars in the LLM/summary settings if your model context allows it","Limit what is sent: use CSS/xpath filters or ignore-text rules on the watch so the diff shrinks","Catch LLMInputTooLargeError and skip AI summarisation for oversized changes"],"exampleFix":"# before\nsummary = summarise_change(diff, max_chars=50000)\n# after\ntry:\n    summary = summarise_change(diff, max_chars=50000)\nexcept LLMInputTooLargeError:\n    summary = None  # change too big to summarise","handlingStrategy":"type-guard","validationCode":"if len(diff_text) > max_chars:\n    diff_text = None  # skip AI summary for oversized change","typeGuard":"def summarisable(text: str, max_chars: int) -> bool:\n    return len(text) <= max_chars","tryCatchPattern":"from changedetectionio.llm.evaluator import LLMInputTooLargeError\ntry:\n    summary = summarise_change(diff, max_chars=mc)\nexcept LLMInputTooLargeError:\n    summary = None","preventionTips":["Pre-check len(text) against the configured max_chars","Use watch filters (CSS/xpath/ignore) to keep diffs small","Treat AI summary as optional enrichment, never a required step"],"tags":["llm","ai-summary","size-limit","input-validation"],"backgroundTag":"payload-too-large","analyzedSha":"5d9c7c6da76340597243e8163c4f2439237fa0e8","analyzedAt":"2026-08-27T19:41:16.067Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}