dgtlmoon/changedetection.io · error · ProcessorException
Unable to extract restock data for this page unfortunately.
Error message
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. What it means
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.
Source
Thrown at changedetectionio/processors/restock_diff/processor.py:587
# Main detection method
fetched_md5 = None
# Maintain 'last_price' = the price from *before the last actual price change*, for the
# watch-list up/down arrow (get_price_change_percent). Only move it when the price really
# changed; on an unchanged check we MUST preserve it, otherwise frequent re-checks of a
# stable price would overwrite last_price with the current price and the arrow would vanish.
# Display only - the % threshold/change detection below compares against the stored 'price'.
old_restock = watch.get('restock') or {}
old_price = old_restock.get('price')
new_price = update_obj['restock'].get('price')
if new_price is not None and new_price != old_price:
update_obj['restock']['last_price'] = old_price # price moved: remember what we moved from (None on first detection)
logger.debug(f"{watch.get('uuid')} price changed '{old_price}' -> '{new_price}', setting 'last_price' to '{old_price}'.")
else:
update_obj['restock']['last_price'] = old_restock.get('last_price') # unchanged: keep the existing reference
if not self.fetcher.instock_data and not itemprop_availability.get('availability') and not itemprop_availability.get('price'):
raise ProcessorException(
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.",
url=watch.get('url'),
status_code=self.fetcher.get_last_status_code(),
screenshot=self.fetcher.screenshot,
xpath_data=self.fetcher.xpath_data
)
logger.debug(f"self.fetcher.instock_data is - '{self.fetcher.instock_data}' and itemprop_availability.get('availability') is {itemprop_availability.get('availability')}")
# Nothing automatic in microdata found, revert to scraping the page
if self.fetcher.instock_data and itemprop_availability.get('availability') is None:
# 'Possibly in stock' comes from stock-not-in-stock.js when no string found above the fold.
# Careful! this does not really come from chrome/js when the watch is set to plaintext
update_obj['restock']["in_stock"] = True if self.fetcher.instock_data == 'Possibly in stock' else False
logger.debug(f"Watch UUID {watch.get('uuid')} restock check returned instock_data - '{self.fetcher.instock_data}' from JS scraper.")
# Very often websites will lie about the 'availability' in the metadata, so if the scraped version says its NOT in stock, use that.
if self.fetcher.instock_data and self.fetcher.instock_data != 'Possibly in stock':
if update_obj['restock'].get('in_stock'):View on GitHub (pinned to 5d9c7c6da7)
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
Defensive patterns
Strategy: fallback
Validate before calling
# preflight: confirm the page exposes stock signals before relying on restock_diff
import requests
html = requests.get(url, timeout=30).text
if not any(k in html.lower() for k in ('availability', 'price', 'instock')):
watch['fetch_backend'] = 'playwright' # JS rendering required Try / catch
try:
handler.run_changedetection(watch, ...)
except ProcessorException as e:
if 'Unable to extract restock data' in str(e):
watch['fetch_backend'] = 'playwright' # retry with JS rendering
requeue(uuid) Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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).
Related errors
AI-assisted analysis of dgtlmoon/changedetection.io@5d9c7c6da7 (2026-08-27).
Data as JSON: /api/errors/e70ee79e721b8c95.
Report an issue: GitHub.