{"record":{"id":"45d95589dfc68f34","repo":"dgtlmoon/changedetection.io","slug":"uuid-watch-get-uuid-screenshot-comparison","errorCode":null,"errorMessage":"UUID: {watch.get('uuid')} - Screenshot comparison failed: {e}","messagePattern":"UUID: (.+?) - Screenshot comparison failed: (.+?)","errorType":"exception","errorClass":"ProcessorException","httpStatus":null,"severity":"error","filePath":"changedetectionio/processors/image_ssim_diff/processor.py","lineNumber":226,"sourceCode":"            thread.start()\n            thread.join(timeout=60)\n\n            if exception_container[0]:\n                raise exception_container[0]\n\n            # Subprocess returns only the change score - we decide if it's a \"change\"\n            change_score = result_container[0]\n            if change_score is None:\n                raise RuntimeError(\"Image comparison subprocess returned no result\")\n\n            changed_detected = change_score > min_change_percentage\n            logger.info(f\"UUID: {watch.get('uuid')} -  {process_screenshot_handler.IMPLEMENTATION_NAME}: {change_score:.2f}% pixels changed, pixel_diff_threshold_sensitivity: {pixel_difference_threshold_sensitivity:.0f} score={change_score:.2f}%, min_change_threshold={min_change_percentage}%\")\n\n        except Exception as e:\n            logger.error(f\"UUID: {watch.get('uuid')} - Failed to compare screenshots: {e}\")\n            logger.trace(f\"UUID: {watch.get('uuid')} - Processed in {time.time() - now:.3f}s\")\n\n            raise ProcessorException(\n                message=f\"UUID: {watch.get('uuid')} - Screenshot comparison failed: {e}\",\n                url=watch.get('url')\n            )\n\n        # Return results\n        update_obj = {\n            'previous_md5': hashlib.md5(self.screenshot).hexdigest(),\n            'last_error': False\n        }\n\n        if changed_detected:\n            logger.info(f\"UUID: {watch.get('uuid')} - Change detected using OpenCV! Score: {change_score:.2f}\")\n        else:\n            logger.debug(f\"UUID: {watch.get('uuid')} - No significant change using OpenCV. Score: {change_score:.2f}\")\n        logger.trace(f\"UUID: {watch.get('uuid')} - Processed in {time.time() - now:.3f}s\")\n\n        return changed_detected, update_obj, self.screenshot\n","sourceCodeStart":208,"sourceCodeEnd":244,"githubUrl":"https://github.com/dgtlmoon/changedetection.io/blob/5d9c7c6da76340597243e8163c4f2439237fa0e8/changedetectionio/processors/image_ssim_diff/processor.py#L208-L244","documentation":"A catch-all ProcessorException raised in run_changedetection of the image_ssim_diff processor. Any unexpected exception thrown by the screenshot comparison handler (decode, crop, SSIM computation, Pillow errors, etc.) is logged and re-raised as a ProcessorException with the watch UUID and the original error text appended.","triggerScenarios":"The screenshot-comparison code path raising: corrupt/truncated screenshot bytes fed to Pillow, an invalid crop region/element selection (e.g. element selector matched nothing so crop box is empty), incompatible Pillow/numpy versions, or zero-dimension images after cropping.","commonSituations":"The watched page's element selector no longer matches (crop region empty); a site returning an HTML error page saved as screenshot; upgrading Pillow/numpy and hitting API breakage; extremely large screenshots causing memory errors.","solutions":["Read the appended original error '{e}' — it names the real cause (e.g. 'Image cannot be cropped', 'cannot identify image file')","Verify the element selector / drawn region still resolves to a non-empty area on the page","Test with the region cleared (whole-page compare) to isolate crop-related failures","Pin/align Pillow and numpy versions known to work with the SSIM handler","Re-capture the snapshot: delete the watch's snapshot dir so a fresh screenshot is stored"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    changed, update_obj, shot = handler.run_changedetection(watch, ...)\nexcept ProcessorException as e:\n    logger.error('SSIM check failed for %s: %s', watch['uuid'], e)\n    # mark watch errored, keep previous snapshot, do not retry blindly\n    watch['last_error'] = str(e)","preventionTips":["Validate element selectors regularly (the built-in preview) so crop regions stay non-empty","Keep Pillow/numpy versions aligned with the project's requirements","Treat the appended inner message as the real diagnostic, not the wrapper text"],"tags":["screenshot","ssim","processor","wrapper-exception"],"backgroundTag":"image-processing-failed","analyzedSha":"5d9c7c6da76340597243e8163c4f2439237fa0e8","analyzedAt":"2026-08-27T19:41:16.067Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}