dgtlmoon/changedetection.io · warning · FilterNotFoundInResponse
{self.filter_config.include_filters}
Error message
{self.filter_config.include_filters} What it means
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.
Source
Thrown at changedetectionio/processors/text_json_diff/processor.py:382
# JSON filters
elif any(filter_rule.startswith(prefix) for prefix in JSON_FILTER_PREFIXES):
filtered_content += html_tools.extract_json_as_string(
content=content,
json_filter=filter_rule
)
# CSS selectors, default fallback
else:
filtered_content += html_tools.include_filters(
include_filters=filter_rule,
html_content=content,
append_pretty_line_formatting=not self.watch.is_source_type_url
)
# Raise error if filter returned nothing
if not filtered_content.strip():
raise FilterNotFoundInResponse(
msg=self.filter_config.include_filters,
screenshot=self.fetcher.screenshot,
xpath_data=self.fetcher.xpath_data
)
return filtered_content
def apply_subtractive_selectors(self, content):
"""Remove elements matching subtractive selectors."""
return html_tools.element_removal(self.filter_config.subtractive_selectors, content)
def extract_text_from_html(self, html_content, stream_content_type):
"""Convert HTML to plain text."""
do_anchor = self.datastore.data["settings"]["application"].get("render_anchor_tag_content", False)
return html_tools.html_to_text(
html_content=html_content,
render_anchor_tag_content=do_anchor,View on GitHub (pinned to 5d9c7c6da7)
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
Example fix
# before include_filters: "//span[@class='price-old-2023']" # after include_filters: "//span[contains(@class,'price')]"
Defensive patterns
Strategy: try-catch
Validate before calling
# smoke-test the selector before relying on the watch
from lxml import html as lh
doc = lh.fromstring(requests.get(url, timeout=30).content)
if not doc.xpath(filter_string):
print('filter matches nothing on current page') Try / catch
try:
handler.run_changedetection(watch, ...)
except FilterNotFoundInResponse as e:
notify(f"filter no longer matches: {e}") # e.msg holds the filter list Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- XPath not permitted in this field!
- Invalid JSON object for field: {value}
- Validation failed: {'; '.join(error_details)}
- Content Fetcher > xPath scraper failed. Please report this U
- '%(expression)s' is not a valid XPath expression. (%(error)s
AI-assisted analysis of dgtlmoon/changedetection.io@5d9c7c6da7 (2026-08-27).
Data as JSON: /api/errors/2776729604d829c3.
Report an issue: GitHub.