{"record":{"id":"e70ee79e721b8c95","repo":"dgtlmoon/changedetection.io","slug":"unable-to-extract-restock-data-for-this-page-unfor","errorCode":null,"errorMessage":"Unable to extract restock data for this page unfortunately. (Got code {self.fetcher.get_last_status_code()} from server), no embedded stock information was found and nothing interesting in the text, try using this watch with Chrome.","messagePattern":"Unable to extract restock data for this page unfortunately\\. \\(Got code (.+?) from server\\), no embedded stock information was found and nothing interesting in the text, try using this watch with Chrome\\.","errorType":"exception","errorClass":"ProcessorException","httpStatus":null,"severity":"error","filePath":"changedetectionio/processors/restock_diff/processor.py","lineNumber":587,"sourceCode":"        # Main detection method\n        fetched_md5 = None\n\n        # Maintain 'last_price' = the price from *before the last actual price change*, for the\n        # watch-list up/down arrow (get_price_change_percent). Only move it when the price really\n        # changed; on an unchanged check we MUST preserve it, otherwise frequent re-checks of a\n        # stable price would overwrite last_price with the current price and the arrow would vanish.\n        # Display only - the % threshold/change detection below compares against the stored 'price'.\n        old_restock = watch.get('restock') or {}\n        old_price = old_restock.get('price')\n        new_price = update_obj['restock'].get('price')\n        if new_price is not None and new_price != old_price:\n            update_obj['restock']['last_price'] = old_price          # price moved: remember what we moved from (None on first detection)\n            logger.debug(f\"{watch.get('uuid')} price changed '{old_price}' -> '{new_price}', setting 'last_price' to '{old_price}'.\")\n        else:\n            update_obj['restock']['last_price'] = old_restock.get('last_price')  # unchanged: keep the existing reference\n\n        if not self.fetcher.instock_data and not itemprop_availability.get('availability') and not itemprop_availability.get('price'):\n            raise ProcessorException(\n                message=f\"Unable to extract restock data for this page unfortunately. (Got code {self.fetcher.get_last_status_code()} from server), no embedded stock information was found and nothing interesting in the text, try using this watch with Chrome.\",\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        logger.debug(f\"self.fetcher.instock_data is - '{self.fetcher.instock_data}' and itemprop_availability.get('availability') is {itemprop_availability.get('availability')}\")\n        # Nothing automatic in microdata found, revert to scraping the page\n        if self.fetcher.instock_data and itemprop_availability.get('availability') is None:\n            # 'Possibly in stock' comes from stock-not-in-stock.js when no string found above the fold.\n            # Careful! this does not really come from chrome/js when the watch is set to plaintext\n            update_obj['restock'][\"in_stock\"] = True if self.fetcher.instock_data == 'Possibly in stock' else False\n            logger.debug(f\"Watch UUID {watch.get('uuid')} restock check returned instock_data - '{self.fetcher.instock_data}' from JS scraper.\")\n\n        # Very often websites will lie about the 'availability' in the metadata, so if the scraped version says its NOT in stock, use that.\n        if self.fetcher.instock_data and self.fetcher.instock_data != 'Possibly in stock':\n            if update_obj['restock'].get('in_stock'):","sourceCodeStart":569,"sourceCodeEnd":605,"githubUrl":"https://github.com/dgtlmoon/changedetection.io/blob/5d9c7c6da76340597243e8163c4f2439237fa0e8/changedetectionio/processors/restock_diff/processor.py#L569-L605","documentation":"A ProcessorException raised by the restock_diff processor when it found no availability signal at all: fetcher.instock_data is empty, no schema.org itemprop availability was extracted, and no price was found. The processor refuses to record a meaningless 'no data' state and tells the user the page yielded nothing usable.","triggerScenarios":"The fetched page contains no JSON-LD/microdata stock or price info and no recognizable price/availability text — typically because the content is rendered client-side by JavaScript that the plain HTTP fetcher never executes.","commonSituations":"SPA shops (React/Vue) whose HTML shell has no stock markup; being served a bot-check/consent page; wrong region/currency page with no price text; page returned an error status (see the embedded status code in the message).","solutions":["Enable a real browser (Playwright/Chrome) for the watch so JS renders stock info, as the message suggests","Check the embedded HTTP status code — a 403/429 means bot blocking, fix that first (proxies, headers, wait times)","Add an include filter or CSS/XPath element that isolates the stock/price node on the page","Target the site's underlying JSON API endpoint instead of the rendered page"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"# preflight: confirm the page exposes stock signals before relying on restock_diff\nimport requests\nhtml = requests.get(url, timeout=30).text\nif not any(k in html.lower() for k in ('availability', 'price', 'instock')):\n    watch['fetch_backend'] = 'playwright'  # JS rendering required","typeGuard":null,"tryCatchPattern":"try:\n    handler.run_changedetection(watch, ...)\nexcept ProcessorException as e:\n    if 'Unable to extract restock data' in str(e):\n        watch['fetch_backend'] = 'playwright'  # retry with JS rendering\n        requeue(uuid)","preventionTips":["Enable a real browser for SPA sites from the start","Check the embedded HTTP status in the message for bot-blocking before blaming extraction","Use include filters to scope extraction to the product block"],"tags":["restock","availability","javascript-rendering","no-data"],"backgroundTag":"no-extractable-data","analyzedSha":"5d9c7c6da76340597243e8163c4f2439237fa0e8","analyzedAt":"2026-08-27T19:41:16.067Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}