{"record":{"id":"2776729604d829c3","repo":"dgtlmoon/changedetection.io","slug":"self-filter-config-include-filters","errorCode":null,"errorMessage":"{self.filter_config.include_filters}","messagePattern":"\\{self\\.filter_config\\.include_filters\\}","errorType":"exception","errorClass":"FilterNotFoundInResponse","httpStatus":null,"severity":"warning","filePath":"changedetectionio/processors/text_json_diff/processor.py","lineNumber":382,"sourceCode":"\n            # JSON filters\n            elif any(filter_rule.startswith(prefix) for prefix in JSON_FILTER_PREFIXES):\n                filtered_content += html_tools.extract_json_as_string(\n                    content=content,\n                    json_filter=filter_rule\n                )\n\n            # CSS selectors, default fallback\n            else:\n                filtered_content += html_tools.include_filters(\n                    include_filters=filter_rule,\n                    html_content=content,\n                    append_pretty_line_formatting=not self.watch.is_source_type_url\n                )\n\n        # Raise error if filter returned nothing\n        if not filtered_content.strip():\n            raise FilterNotFoundInResponse(\n                msg=self.filter_config.include_filters,\n                screenshot=self.fetcher.screenshot,\n                xpath_data=self.fetcher.xpath_data\n            )\n\n        return filtered_content\n\n    def apply_subtractive_selectors(self, content):\n        \"\"\"Remove elements matching subtractive selectors.\"\"\"\n        return html_tools.element_removal(self.filter_config.subtractive_selectors, content)\n\n    def extract_text_from_html(self, html_content, stream_content_type):\n        \"\"\"Convert HTML to plain text.\"\"\"\n        do_anchor = self.datastore.data[\"settings\"][\"application\"].get(\"render_anchor_tag_content\", False)\n\n        return html_tools.html_to_text(\n            html_content=html_content,\n            render_anchor_tag_content=do_anchor,","sourceCodeStart":364,"sourceCodeEnd":400,"githubUrl":"https://github.com/dgtlmoon/changedetection.io/blob/5d9c7c6da76340597243e8163c4f2439237fa0e8/changedetectionio/processors/text_json_diff/processor.py#L364-L400","documentation":"FilterNotFoundInResponse raised by apply_include_filters in the text_json_diff processor when the combined output of all include filters (CSS/XPath/regex) is empty or whitespace-only. The message embeds the filter list that matched nothing, plus the screenshot and xpath_data for UI display.","triggerScenarios":"Calling a check with include_filters set while every filter expression evaluates to nothing on the current page: selector no longer present, XPath returns an empty node-set, or regex has no match. Note that non-empty whitespace output also triggers it because of the .strip() check.","commonSituations":"Site redesign removed/renamed the element your selector targets; page served differently to the scraper (bot page, geo variant) so the node is absent; overly specific XPath broken by minor markup changes; regex with a typo or anchored to text that changed.","solutions":["Re-inspect the live page and update the include filter to a stable selector (prefer data-* attributes or ids)","Loosen the filter (e.g. match an ancestor container) or add a fallback filter line","Check the watch's debug/screenshot to see what page was actually fetched — fix bot-blocking first if that's the cause","Remove the include filter entirely if you want whole-page monitoring"],"exampleFix":"# before\ninclude_filters: \"//span[@class='price-old-2023']\"\n# after\ninclude_filters: \"//span[contains(@class,'price')]\"","handlingStrategy":"try-catch","validationCode":"# smoke-test the selector before relying on the watch\nfrom lxml import html as lh\ndoc = lh.fromstring(requests.get(url, timeout=30).content)\nif not doc.xpath(filter_string):\n    print('filter matches nothing on current page')","typeGuard":null,"tryCatchPattern":"try:\n    handler.run_changedetection(watch, ...)\nexcept FilterNotFoundInResponse as e:\n    notify(f\"filter no longer matches: {e}\")  # e.msg holds the filter list","preventionTips":["Prefer resilient selectors (ids, data-*) over class chains that redesigns break","Set up the built-in 'filter not found' notification so you learn immediately","Keep a secondary broader filter line as a fallback — any non-empty result avoids the error"],"tags":["include-filter","xpath","css-selector","processor"],"backgroundTag":"selector-not-found","analyzedSha":"5d9c7c6da76340597243e8163c4f2439237fa0e8","analyzedAt":"2026-08-27T19:41:16.067Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}