dgtlmoon/changedetection.io · warning

No Favicon available for {uuid}

Error message

No Favicon available for {uuid}

What it means

Flask abort(404) raised by the watch favicon endpoint when no favicon file exists in the watch's data directory. changedetection.io stores a per-watch favicon (scraped or fetched) in watch.data_dir; when a browser requests /favicon/<uuid> and no such file has been saved yet, the handler falls through to this 404 with a descriptive message.

Source

Thrown at changedetectionio/api/Watch.py:459

        watch = self.datastore.data['watching'].get(uuid)
        if not watch:
            abort(404, message=f"No watch exists with the UUID of {uuid}")

        favicon_filename = watch.get_favicon_filename()
        if favicon_filename:
            # Use cached MIME type detection
            filepath = os.path.join(watch.data_dir, favicon_filename)
            mime = get_favicon_mime_type(filepath)
            if 'text' in mime:
                logger.debug(f"Aborting favicon request for {filepath} because mimetype might be text (bad mimetype) '{mime}'")
                abort(404)

            response = make_response(send_from_directory(watch.data_dir, favicon_filename))
            response.headers['Content-type'] = mime
            response.headers['Cache-Control'] = 'max-age=300, must-revalidate'  # Cache for 5 minutes, then revalidate
            return response

        abort(404, message=f'No Favicon available for {uuid}')


class CreateWatch(Resource):
    def __init__(self, **kwargs):
        # datastore is a black box dependency
        self.datastore = kwargs['datastore']
        self.update_q = kwargs['update_q']

    @auth.check_token
    @validate_openapi_request('createWatch')
    def post(self):
        """Create a single watch."""

        # Silently discard `__`-prefixed transient/internal keys (not part of the public schema).
        json_data = strip_internal_api_fields(request.get_json())
        url = json_data['url'].strip()

        if not is_safe_valid_url(url):

View on GitHub (pinned to 5d9c7c6da7)

Solutions

  1. Verify the watch has run at least one successful check so a favicon can be captured
  2. Check watch.data_dir on disk for the favicon file returned by watch.get_favicon_filename()
  3. Re-fetch the favicon by triggering a recheck of the watch
  4. If serving favicons is optional in your UI, tolerate 404 and render a placeholder icon

Example fix

# before
resp = requests.get(f'{base}/favicon/{uuid}')
resp.raise_for_status()

# after
resp = requests.get(f'{base}/favicon/{uuid}')
if resp.status_code == 404:
    return placeholder_icon  # favicon not yet captured
resp.raise_for_status()
Defensive patterns

Strategy: fallback

Validate before calling

import os, requests
def favicon_exists(base, uuid):
    # cheap probe: 404 means not captured yet
    return requests.head(f'{base}/favicon/{uuid}').status_code == 200

Try / catch

try:
    resp = requests.get(f'{base}/favicon/{uuid}', timeout=10)
    resp.raise_for_status()
except requests.HTTPError as e:
    if e.response.status_code == 404:
        return DEFAULT_ICON
    raise

Prevention

When it happens

Trigger: GET request to the watch favicon URL (e.g. /favicon/<uuid>) before any favicon was captured for the watch, after the favicon file was deleted from disk (data dir pruned/migrated), or when the fetch of the favicon failed during watch checks.

Common situations: Freshly created watches that have never run a check; watches whose data_dir was copied/restored without favicon files; watchdog/browserless not running so favicons never get captured.

Related errors


AI-assisted analysis of dgtlmoon/changedetection.io@5d9c7c6da7 (2026-08-27). Data as JSON: /api/errors/fc439f69d05d07e7. Report an issue: GitHub.