Preconditions and environment
Steps to reproduce
- Add a "Catalog Products List" widget to any CMS page (e.g. homepage).
- Open that page with a malformed UTF-8 byte sequence in a query parameter, e.g.:
https://example.test/?q=%C0%AF (overlong UTF-8 slash)
https://example.test/?dclid=%EDclid
https://example.test/?utm_source=%E0
https://example.test/?fbclid=foo% (dangling percent)
- Observe the rendered page and
var/log/system.log.
These URLs occur in the wild from truncated Facebook / Google Ads tracking parameters (fbclid, gclid, dclid, utm_*), broken share buttons, and bot scanners.
Expected result
Widget renders normally. Malformed UTF-8 in the query string must not break server-side block rendering.
Actual result
Widget output is replaced by the generic CMS fallback message ("Sorry, we cannot generate the content...", localized) and the following entry is logged:
main.CRITICAL: InvalidArgumentException: Unable to serialize value.
Error: Malformed UTF-8 characters, possibly incorrectly encoded
in vendor/magento/framework/Serialize/Serializer/Json.php:26
Abbreviated stack trace:
#0 vendor/magento/module-catalog-widget/Block/Product/ProductsList.php(235):
Magento\Framework\Serialize\Serializer\Json->serialize()
#5 vendor/magento/framework/View/Element/AbstractBlock.php(1063):
ProductsList->getCacheKeyInfo()
#6 AbstractBlock.php(1150): AbstractBlock->getCacheKey()
#7 AbstractBlock.php(676): AbstractBlock->_loadCache()
#8 Interceptor->___callParent() → toHtml()
...
#82 vendor/magento/framework/App/Http.php(120): renderResult()
#92 pub/index.php(30): Bootstrap->run()
Root cause
Magento\CatalogWidget\Block\Product\ProductsList::getCacheKeyInfo() (line 235 of current 2.4-develop) passes raw $_GET (via $this->getRequest()->getParams()) to the serializer:
return [
...
$this->json->serialize($this->getRequest()->getParams()),
...
];
Magento\Framework\Serialize\Serializer\Json::serialize() calls json_encode($value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) with no JSON_INVALID_UTF8_SUBSTITUTE / JSON_INVALID_UTF8_IGNORE flag. Any byte sequence in $_GET that is not valid UTF-8 causes json_encode to return false with JSON_ERROR_UTF8, which the serializer rethrows as InvalidArgumentException.
Because getCacheKeyInfo() is called from AbstractBlock::getCacheKey() → _loadCache() → toHtml(), the entire block fails to render and Magento falls back to the generic error template.
The same underlying defect surfaces in any other code path that serializes user-controlled input (request params, raw cookie values, certain redirect URLs). See also the ReCaptcha variant reported in #30486.
Related / prior reports
Suggested directions (not a fix proposal)
Listed only to clarify scope of the regression:
Magento\Framework\Serialize\Serializer\Json::serialize() — pass JSON_INVALID_UTF8_SUBSTITUTE (or wrap in a guard that returns a safe placeholder).
Magento\CatalogWidget\Block\Product\ProductsList::getCacheKeyInfo() — sanitize / hash $_GET instead of serializing it raw. Including the full $_GET in the cache key is also a separate performance / cache-fragmentation concern, since any unique URL parameter creates a new cache entry.
Severity / impact
- Reliability: any URL with malformed UTF-8 from third-party traffic sources renders an error region instead of the home / category / CMS page where the widget is placed.
- Reachability: trivial. A single crafted GET parameter is enough; no auth, no preconditions beyond the widget being on the page.
- Observability cost: every hit logs a
CRITICAL to system.log, which can drown application logs under bot traffic.
Preconditions and environment
Magento\CatalogWidget\Block\Product\ProductsList(or its rewrites / interceptors)Steps to reproduce
https://example.test/?q=%C0%AF(overlong UTF-8 slash)https://example.test/?dclid=%EDclidhttps://example.test/?utm_source=%E0https://example.test/?fbclid=foo%(dangling percent)var/log/system.log.These URLs occur in the wild from truncated Facebook / Google Ads tracking parameters (
fbclid,gclid,dclid,utm_*), broken share buttons, and bot scanners.Expected result
Widget renders normally. Malformed UTF-8 in the query string must not break server-side block rendering.
Actual result
Widget output is replaced by the generic CMS fallback message (
"Sorry, we cannot generate the content...", localized) and the following entry is logged:Abbreviated stack trace:
Root cause
Magento\CatalogWidget\Block\Product\ProductsList::getCacheKeyInfo()(line 235 of current2.4-develop) passes raw$_GET(via$this->getRequest()->getParams()) to the serializer:Magento\Framework\Serialize\Serializer\Json::serialize()callsjson_encode($value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)with noJSON_INVALID_UTF8_SUBSTITUTE/JSON_INVALID_UTF8_IGNOREflag. Any byte sequence in$_GETthat is not valid UTF-8 causesjson_encodeto returnfalsewithJSON_ERROR_UTF8, which the serializer rethrows asInvalidArgumentException.Because
getCacheKeyInfo()is called fromAbstractBlock::getCacheKey()→_loadCache()→toHtml(), the entire block fails to render and Magento falls back to the generic error template.The same underlying defect surfaces in any other code path that serializes user-controlled input (request params, raw cookie values, certain redirect URLs). See also the ReCaptcha variant reported in #30486.
Related / prior reports
MC-23846, but follow-up from @edas11 (2025-05-06) confirms reproduction on 2.4.7-p5 and 2.4.4-p13. This issue is filed to track the regression / incomplete fix.Magento_ReCaptcha. Closed 2021.getSerializedCheckoutConfig(silentjson_encodefailure).Suggested directions (not a fix proposal)
Listed only to clarify scope of the regression:
Magento\Framework\Serialize\Serializer\Json::serialize()— passJSON_INVALID_UTF8_SUBSTITUTE(or wrap in a guard that returns a safe placeholder).Magento\CatalogWidget\Block\Product\ProductsList::getCacheKeyInfo()— sanitize / hash$_GETinstead of serializing it raw. Including the full$_GETin the cache key is also a separate performance / cache-fragmentation concern, since any unique URL parameter creates a new cache entry.Severity / impact
CRITICALtosystem.log, which can drown application logs under bot traffic.