dgtlmoon/changedetection.io · error · Non200ErrorCodeReceived
Non200ErrorCodeReceived
Error message
Non200ErrorCodeReceived
What it means
After the jq import succeeds, the input is checked with validate_jq_expression() and compiled with jq.compile(). If either raises ValueError, the validator reports the expression as invalid jq, embedding jq's compiler error message.
Source
Thrown at changedetectionio/content_fetchers/playwright.py:359
except Exception as e:
# https://github.com/dgtlmoon/changedetection.io/discussions/2122#discussioncomment-8241962
logger.critical(f"Response from the browser/Playwright did not have a status_code! Response follows.")
logger.critical(response)
await context.close()
await browser.close()
raise PageUnloadable(url=url, status_code=None, message=str(e))
if fetch_favicon:
try:
self.favicon_blob = await self.page.evaluate(FAVICON_FETCHER_JS)
await self.page.request_gc()
except Exception as e:
logger.error(f"Error fetching FavIcon info {str(e)}, continuing.")
if self.status_code != 200 and not ignore_status_codes:
screenshot = await capture_full_page_async(self.page, screenshot_format=self.screenshot_format, watch_uuid=watch_uuid, lock_viewport_elements=self.lock_viewport_elements)
# Finally block will handle cleanup
raise Non200ErrorCodeReceived(url=url, status_code=self.status_code, screenshot=screenshot)
if not empty_pages_are_a_change and len((await self.page.content()).strip()) == 0:
logger.debug("Content Fetcher > Content was empty, empty_pages_are_a_change = False")
await context.close()
await browser.close()
raise EmptyReply(url=url, status_code=response.status)
# Wrap remaining operations in try/finally to ensure cleanup
try:
# Run Browser Steps here
if self.browser_steps:
try:
await self.iterate_browser_steps(start_url=url)
except BrowserStepsStepException:
# Finally block will handle cleanup
raise
await self.page.wait_for_timeout(extra_wait * 1000)View on GitHub (pinned to 5d9c7c6da7)
Solutions
- Test the filter with the jq CLI or `python -c "import jq; jq.compile('...')"`
- Simplify the filter; check braces/quotes are balanced
- Ensure validate_jq_expression limits (length, banned tokens) aren't being hit and shorten the program
Example fix
# before jq:.prices[] | select(.amount > 100 and # after jq:.prices[] | select(.amount > 100)
Defensive patterns
Strategy: validation
Validate before calling
import jq
from changedetectionio.html_tools import validate_jq_expression
def jq_ok(expr: str) -> bool:
try:
validate_jq_expression(expr)
jq.compile(expr)
return True
except ValueError:
return False Type guard
def is_valid_jq(expr: str) -> bool:
try:
jq.compile(expr)
return True
except (ValueError, Exception):
return False Try / catch
try:
jq.compile(expr)
except ValueError as e:
# str(e) contains jq's compile error for display
... Prevention
- Test filters with the jq CLI first
- Keep programs short — built-in validation enforces size/complexity limits
- Balance braces/quotes; remember jq is a program not a path language
When it happens
Trigger: Submitting 'jq:' lines with syntax errors such as 'jq:.foo bar', unbalanced braces 'jq:{a:', or a guard-rules violation flagged by validate_jq_expression (e.g. disallowed constructs) — anything that makes jq.compile raise ValueError.
Common situations: Typos in jq filter programs; using jq CLI-only syntax not supported by the python binding's version; overly long programs caught by the built-in safety validation (validate_jq_expression enforces length/complexity limits).
Related errors
- Invalid JSON object for field: {value}
- Backup archive decompressed size ({total_uncompressed // (10
- Zip Slip path traversal detected in backup archive: {member.
- BrowserStepsStepException
- EmptyReply
AI-assisted analysis of dgtlmoon/changedetection.io@5d9c7c6da7 (2026-08-27).
Data as JSON: /api/errors/0226960ca500e98f.
Report an issue: GitHub.