dgtlmoon/changedetection.io · warning

Asset '{asset_name}' not found

Error message

Asset '{asset_name}' not found

What it means

abort(404) in the processor asset endpoint when the processor's get_asset() hook ran successfully but returned None, meaning the named asset does not exist for that watch. Assets are processor-served binaries (e.g. last screenshot, embedded media) resolved by name per watch.

Source

Thrown at changedetectionio/blueprint/ui/diff.py:614

        # Get the processor type for this watch
        processor_name = watch.get('processor', 'text_json_diff')

        # Try to get the processor's difference module (works for both built-in and plugin processors)
        from changedetectionio.processors import get_processor_submodule
        processor_module = get_processor_submodule(processor_name, 'difference')

        # Call the processor's get_asset() function
        if processor_module and hasattr(processor_module, 'get_asset'):
            result = processor_module.get_asset(
                asset_name=asset_name,
                watch=watch,
                datastore=datastore,
                request=request
            )

            if result is None:
                from flask import abort
                abort(404, description=f"Asset '{asset_name}' not found")

            binary_data, content_type, cache_control = result

            response = make_response(binary_data)
            response.headers['Content-Type'] = content_type
            if cache_control:
                response.headers['Cache-Control'] = cache_control
            return response
        else:
            logger.warning(f"Processor {processor_name} does not implement get_asset()")
            from flask import abort
            abort(404, description=f"Processor '{processor_name}' does not support assets")

    return diff_blueprint

View on GitHub (pinned to 5d9c7c6da7)

Solutions

  1. Trigger a recheck so the processor regenerates the asset
  2. Verify the exact asset name from the watch's current data (not from an outdated UI cache)
  3. Check the watch's data dir on disk to confirm the asset file exists

Example fix

# before
resp = requests.get(f'{base}/diff/{uuid}/asset/last-screenshot.png')
resp.raise_for_status()

# after
resp = requests.get(f'{base}/diff/{uuid}/asset/last-screenshot.png')
if resp.status_code == 404:
    trigger_recheck(uuid); time.sleep(poll_interval)
resp.raise_for_status()
Defensive patterns

Strategy: fallback

Validate before calling

import os
def asset_exists(watch, asset_name):
    # processor assets typically live in the watch data dir
    return os.path.isfile(os.path.join(watch.data_dir, asset_name))

Try / catch

try:
    r = requests.get(asset_url)
    r.raise_for_status()
except requests.HTTPError as e:
    if e.response.status_code == 404:
        return None  # asset not present for this watch
    raise

Prevention

When it happens

Trigger: GET /diff/<uuid>/asset/<asset_name> where the processor has get_asset() but no asset with that name exists (never captured, deleted, or wrong name such as a stale filename from history).

Common situations: Requesting a screenshot/asset before the first successful check; referencing an asset name from an old snapshot after files were pruned; changing processors so old asset names are no longer produced.

Related errors


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