#42872·PrestaShop

Invalid form submits fail silently when the controller redirects after addFlashFormErrors()

Author: mattgoudCreated Sep 18, 2026Updated Sep 18, 2026
LabelsBug

Describe the bug

PrestaShopAdminController::addFlashFormErrors() collects form errors without the deep flag:

php
// src/PrestaShopBundle/Controller/Admin/PrestaShopAdminController.php
protected function addFlashFormErrors(FormInterface $form): void
{
    /** @var FormError $formError */
    foreach ($form->getErrors() as $formError) {
        $this->addFlash('error', $formError->getMessage());
    }
}

The deprecated FrameworkBundleAdminController::addFlashFormErrors() that it supersedes uses $form->getErrors(true). Without $deep, Symfony returns only the violations attached to the root form, so every error raised by a constraint declared on a field (the overwhelming majority) is left out. That part is intentional: a controller that re-renders the form displays those child errors inline, and flashing them too would duplicate them.

Every current caller also returns a RedirectResponse on the invalid branch, so the submitted form and its view are discarded too and inline field errors cannot render either. The net result for the merchant is a page that comes back with no success message, no error message, and the value not saved.

Call sites, all extending PrestaShopAdminController, and what each one returns on the invalid branch:

File Action Invalid branch
Improve/Design/ImageSettingsController.php:102 saveSettingsAction redirectToRoute('admin_image_settings_index')
Improve/Design/MailThemeController.php:101,194 generateMailsAction, and the translation form redirectToRoute('admin_mail_theme_index') right after the call
Sell/Order/OrderController.php:1007,1041,1075,2087 order edit sub-forms redirectToRoute('admin_orders_view')
Configure/ShopParameters/OrderStateController.php:168,258 editAction for order status and order return status redirectToRoute('admin_order_states'), inside the isSubmitted() block

Not one of them renders the form again, so the inline error channel is never reached either. These call sites come from reading the code, only the order status case below was executed in the back office.

The helper was introduced in 1265a61e963 with the copy losing the true argument. Present on 9.2.x and develop.

Steps to reproduce

  1. Go to Shop Parameters > Order Settings > Statuses
  2. Edit any status, for instance "Awaiting check payment"
  3. Set the status name to Awaiting check payment <test. The < is rejected by the TypedRegex(TYPE_GENERIC_NAME) constraint that OrderStateType declares on the translated child field, and that constraint has no HTML counterpart, so the browser lets the form through
  4. Click Save

Expected behavior

The rejected value is reported, either as an inline error on the field or as an error flash message, the same way the deprecated base controller did with getErrors(true).

Actual Result

Redirected to the Statuses list with no error message and no success message, and the status name unchanged in ps_order_state_lang. Nothing tells the merchant that the submission was rejected.

Verified on a local docker install (9.2.0, develop): the POST /configure/shop/order-states/1/edit returns a 302, the list page that follows renders no alert at all, and ps_order_state_lang.name still holds the original value.

Note on picking a field to reproduce with

Not every invalid value reaches the server. On Design > Image settings for example, "JPEG compression" carries an HTML max="100", so setting 200 is blocked by native browser validation before the request is sent, even though ImageSettingsType declares a matching Range constraint server side. The bug shows up on fields whose constraint is server side only, such as TypedRegex or Length without a maxlength attribute.

How this was found

Reviewing PrestaShop/PrestaShop#41414, where the migrated Stores contact details form adds field level constraints and hits the same helper, producing the same silent rejection. The mechanism is the base controller helper, not that PR, hence this dedicated issue.

Suggested fix

Direction agreed with @jolelievre in the comments below: the shallow default stays, and making the controllers render the form instead of redirecting is out of scope here, since a redirect after an action can be a deliberate UX choice.

So the fix belongs to the helper: give it an explicit way to also collect child errors, and use it at the call sites that redirect, which today means all 9 of them.

One caveat on the shape. Adding an optional parameter to the existing protected method breaks any subclass that overrides it, PHP fatals on load:

PHP Fatal error: Declaration of C::addFlashFormErrors(FormInterface $form): void must be compatible with PrestaShopAdminController::addFlashFormErrors(FormInterface $form, bool $deep = false): void

PrestaShopAdminController is the base we recommend for module controllers in 9.x, so a separate method, something like addFlashFormErrorsDeep(), reaches the same result without that risk.

PrestaShop version where the bug happens

develop and 9.2.x

How have you installed PrestaShop

git clone, docker environment shipped in the repository