{"record":{"id":"30887797ccd35398","repo":"dgtlmoon/changedetection.io","slug":"cannot-run-more-than-one-price-detected-this-plu","errorCode":null,"errorMessage":"Cannot run, more than one price detected, this plugin is only for product pages with ONE product, try the content-change detection mode.","messagePattern":"Cannot run, more than one price detected, this plugin is only for product pages with ONE product, try the content-change detection mode\\.","errorType":"exception","errorClass":"ProcessorException","httpStatus":null,"severity":"error","filePath":"changedetectionio/processors/restock_diff/processor.py","lineNumber":539,"sourceCode":"                    # pattern as evaluator.py) so they're committed when update_watch runs\n                    if _plugin_tokens:\n                        watch['llm_last_tokens_used'] = _plugin_tokens\n                        watch['llm_tokens_used_cumulative'] = (watch.get('llm_tokens_used_cumulative') or 0) + _plugin_tokens\n\n                    # Plugin provided better data, use it\n                    plugin_has_price = plugin_availability.get('price') is not None\n                    plugin_has_availability = plugin_availability.get('availability') is not None\n\n                    # Only use plugin data if it's actually better than what we have\n                    if plugin_has_price or plugin_has_availability:\n                        itemprop_availability = plugin_availability\n                        logger.info(f\"Using plugin-provided availability data for fetcher '{fetcher_name}' (built-in had price={has_price}, availability={has_availability}; plugin has price={plugin_has_price}, availability={plugin_has_availability})\")\n                if not plugin_availability:\n                    logger.debug(\"No item price/availability from plugins\")\n\n        # If we had multiple prices and plugins also failed, NOW raise the exception\n        if multiple_prices_found and not itemprop_availability.get('price'):\n            raise ProcessorException(\n                message=\"Cannot run, more than one price detected, this plugin is only for product pages with ONE product, try the content-change detection mode.\",\n                url=watch.get('url'),\n                status_code=self.fetcher.get_last_status_code(),\n                screenshot=self.fetcher.screenshot,\n                xpath_data=self.fetcher.xpath_data\n            )\n\n        # Something valid in get_itemprop_availability() by scraping metadata ?\n        if itemprop_availability.get('price') or itemprop_availability.get('availability'):\n            # Store for other usage. Wrap in Restock() so it's ALWAYS a Restock, never a plain\n            # dict: the built-in extruct path returns a Restock, but plugin fallbacks (e.g. the\n            # LLM restock scraper) return a plain dict. A plain dict here later blows up callers\n            # that use its helpers, e.g. watch['restock'].get_price_change_percent() on the\n            # watchlist (AttributeError -> 500 on the list page).\n            update_obj['restock'] = Restock(itemprop_availability)\n\n            if itemprop_availability.get('availability'):\n                # @todo: Configurable?","sourceCodeStart":521,"sourceCodeEnd":557,"githubUrl":"https://github.com/dgtlmoon/changedetection.io/blob/5d9c7c6da76340597243e8163c4f2439237fa0e8/changedetectionio/processors/restock_diff/processor.py#L521-L557","documentation":"A ProcessorException raised by the restock_diff processor when the page contained multiple price candidates and neither built-in extraction nor any plugin could resolve a single price. The restock processor is designed for single-product pages only, so ambiguous pricing aborts the check rather than guessing.","triggerScenarios":"Scraping a category/listing page or product page with variants where multiple price-looking values exist (multiple itemprop price tags, multiple currency patterns in text), and plugin fetchers also returned no definitive price/availability data.","commonSituations":"Pointing the 'stock/price restock' processor at a search results or category page; pages with crossed-out regular price + sale price + per-unit price; marketplace pages listing several offers.","solutions":["Switch the watch's processor to the content-change ('text_json_diff') mode as the message suggests","Narrow the target: add an include filter/element selection covering just the single product's price node","Use a more specific XHR/API URL for the single product instead of the HTML listing page","Write/enable a plugin fetcher that returns a single definitive price"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"from changedetectionio.processors import get_processor_module\n# only use restock_diff on pages you know contain a single product\nif not is_single_product_page(url):\n    watch['processor'] = 'text_json_diff'","typeGuard":null,"tryCatchPattern":"try:\n    handler.run_changedetection(watch, ...)\nexcept ProcessorException as e:\n    if 'more than one price detected' in str(e):\n        watch['processor'] = 'text_json_diff'  # auto-demote as the message suggests","preventionTips":["Reserve restock_diff for single product-detail URLs, never category/search pages","Add a tight include filter around one product's price node","Consider JSON-LD presence a prerequisite: product pages usually expose schema.org offers"],"tags":["restock","price-parsing","multi-product","processor"],"backgroundTag":"ambiguous-data-extraction","analyzedSha":"5d9c7c6da76340597243e8163c4f2439237fa0e8","analyzedAt":"2026-08-27T19:41:16.067Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}