This gave me quite a headache but finally managed to get to the bottom of it; my case is as follow:
Repro:
- add a custom category attribute with
backend_model := Magento\Catalog\Model\Category\Attribute\Backend\Image
- Have the category form save operation fail for whatever reason (e.g. have a plugin on the category model save function which throws an Exception)
Reason:
If you look at https://github.com/magento/magento2/blob/2.4-develop/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php#L240 you'll see that this has the effect of storing the entire POST data of the current form request to session (also the LocalizedException block does the same).
Later on, this data is restored in https://github.com/magento/magento2/blob/2.4-develop/app/code/Magento/Catalog/Controller/Adminhtml/Category/Edit.php#L95 and immediately after the form information for the image attribute is stripped/cleared.
This of course does not handle any custom attribute of Image type we might have defined for our category entity.
Solution:
I added an after* plugin (in adminhtml area only) on \Magento\Framework\Session\SessionManager::__call, where I explicitly check that the invoked method is getCategoryData: if this is the case, I fetch all the custom category image attributes, and strip them from the returned array like Category/Edit does.
This way any further exception message is correctly displayed in the backoffice (granted it extends LocalizedException)