dgtlmoon/changedetection.io · error · ModuleNotFoundError

Processor module '{processor}' not found.

Error message

Processor module '{processor}' not found.

What it means

ModuleNotFoundError raised in async_update_worker when get_processor_module(processor) returns falsy — the watch's 'processor' key names a processor that isn't registered. Unlike a real import failure, this is an explicit 'module not found' guard: the requested processor name has no matching module or plugin.

Source

Thrown at changedetectionio/worker.py:177

                logger.info(f"Worker {worker_id} processing watch UUID {uuid} Priority {queued_item_data.priority} URL {watch['url']}")

                try:
                    # Retrieve signal by name to ensure thread-safe access across worker threads
                    watch_check_update = signal('watch_check_update')
                    watch_check_update.send(watch_uuid=uuid)

                    # Processor is what we are using for detecting the "Change"
                    processor = watch.get('processor', 'text_json_diff')

                    # Init a new 'difference_detection_processor'
                    # Use get_processor_module() to support both built-in and plugin processors
                    from changedetectionio.processors import get_processor_module
                    processor_module = get_processor_module(processor)

                    if not processor_module:
                        error_msg = f"Processor module '{processor}' not found."
                        logger.error(error_msg)
                        raise ModuleNotFoundError(error_msg)

                    update_handler = processor_module.perform_site_check(datastore=datastore,
                                                                         watch_uuid=uuid)

                    # Allow plugins to modify/wrap the update_handler
                    update_handler = apply_update_handler_alter(update_handler, watch, datastore)

                    set_watch_minitext_status(watch, "Fetching...")

                    # All fetchers are now async, so call directly
                    await update_handler.call_browser()

                    # Run change detection in executor to avoid blocking event loop
                    # This includes CPU-intensive operations like HTML parsing (lxml/inscriptis)
                    # which can take 2-10ms and cause GIL contention across workers
                    loop = asyncio.get_event_loop()
                    changed_detected, update_obj, contents = await loop.run_in_executor(
                        executor,

View on GitHub (pinned to 5d9c7c6da7)

Solutions

  1. Reinstall/restore the plugin or dependency that provides that processor, or upgrade back to the version that introduced it
  2. Edit the affected watch(es) and set processor to a core name like 'text_json_diff' (watch.json 'processor' key), or delete the orphaned watch
  3. Check the app startup logs for plugin load errors that silently reduced the processor registry
  4. Verify the exact spelling of the processor name against installed processors before re-assigning

Example fix

# before (watch.json)
"processor": "my_custom_processor"
# after
"processor": "text_json_diff"
Defensive patterns

Strategy: validation

Validate before calling

from changedetectionio.processors import get_processor_module
for uuid, w in datastore.data['watching'].items():
    if not get_processor_module(w.get('processor')):
        print(f'orphaned processor on {uuid}: {w.get("processor")!r}')

Type guard

def has_valid_processor(watch: dict) -> bool:
    from changedetectionio.processors import get_processor_module
    return get_processor_module(watch.get('processor')) is not None

Try / catch

try:
    await run_check(uuid)
except ModuleNotFoundError as e:
    if 'Processor module' in str(e):
        watch = datastore.data['watching'][uuid]
        watch['processor'] = 'text_json_diff'  # repair to a core processor

Prevention

When it happens

Trigger: A watch whose data['processor'] is e.g. 'text_json_diff_missing', a typo'd name, a processor provided by a plugin that was removed/disabled, or a watch created by a newer version using a processor an older instance doesn't know.

Common situations: Downgrading changedetection.io while keeping a datastore containing newer processor names; uninstalling a custom processor plugin but leaving watches assigned to it; manual datastore edits corrupting the processor key; plugin load failure at startup (bad plugin dir).

Related errors


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