dgtlmoon/changedetection.io · error · ScreenshotUnavailable
ScreenshotUnavailable
Error message
ScreenshotUnavailable
What it means
ValidateSimpleURL.__call__ parses the submitted value with urllib.parse.urlparse and requires both a scheme and a netloc; otherwise it raises ValidationError('Invalid URL.'). Empty values pass through so the validator can be paired with validators.Optional().
Source
Thrown at changedetectionio/content_fetchers/playwright.py:420
# Bug 3 in Playwright screenshot handling
# Some bug where it gives the wrong screenshot size, but making a request with the clip set first seems to solve it
# JPEG is better here because the screenshots can be very very large
# Screenshots also travel via the ws:// (websocket) meaning that the binary data is base64 encoded
# which will significantly increase the IO size between the server and client, it's recommended to use the lowest
# acceptable screenshot quality here
# The actual screenshot - this always base64 and needs decoding! horrible! huge CPU usage
self.screenshot = await capture_full_page_async(page=self.page, screenshot_format=self.screenshot_format, watch_uuid=watch_uuid, lock_viewport_elements=self.lock_viewport_elements)
# Force aggressive memory cleanup - screenshots are large and base64 decode creates temporary buffers
await self.page.request_gc()
gc.collect()
except ScreenshotUnavailable:
# Re-raise screenshot unavailable exceptions
raise ScreenshotUnavailable(url=url, status_code=self.status_code)
finally:
# Clean up resources properly with timeouts to prevent hanging
try:
if hasattr(self, 'page') and self.page:
await self.page.request_gc()
await asyncio.wait_for(self.page.close(), timeout=5.0)
logger.debug(f"Successfully closed page for {url}")
except asyncio.TimeoutError:
logger.warning(f"Timed out closing page for {url} (5s)")
except Exception as e:
logger.warning(f"Error closing page for {url}: {e}")
finally:
self.page = None
try:
if context:
await asyncio.wait_for(context.close(), timeout=5.0)View on GitHub (pinned to 5d9c7c6da7)
Solutions
- Prefix the scheme: https://example.com/path
- For non-http URIs, use a different validator or extend the check to allow specific schemes
- Validate client-side before submit and auto-prepend https:// when missing
Example fix
# before example.com/watch # after https://example.com/watch
Defensive patterns
Strategy: validation
Validate before calling
from urllib.parse import urlparse
def simple_url_ok(value: str) -> bool:
if not value:
return True # pair with Optional()
p = urlparse(value)
return bool(p.scheme and p.netloc) Type guard
def is_absolute_http_url(value: str) -> bool:
p = urlparse(value)
return p.scheme in ('http', 'https') and bool(p.netloc) Prevention
- Always include the scheme (https://) in submitted URLs
- Auto-prepend https:// in the UI when the user omits it
- Pair the validator with wtforms.validators.Optional() so blanks pass
When it happens
Trigger: Submitting values like 'example.com/path' (no scheme), 'mailto:someone@x.com' (scheme but no netloc), 'javascript:void(0)', or random text to a field using ValidateSimpleURL. Only strings like 'https://example.com/x' pass.
Common situations: Users omitting https:// when entering a watch URL; entering local paths or hostnames without protocol; passing URIs (urn:, mailto:) that legitimately lack a netloc into a field designed for absolute http(s) URLs.
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
- Non200ErrorCodeReceived
AI-assisted analysis of dgtlmoon/changedetection.io@5d9c7c6da7 (2026-08-27).
Data as JSON: /api/errors/e0a756a5168ed736.
Report an issue: GitHub.