Lack of support for EnforcedResponseException breaks form component submission

Created on 15 July 2025, 5 days ago

Overview

When forms are submitted if a response object is to be enforced, an EnforcedResponseException is thrown by core.

From core/lib/Drupal/Core/Form/EnforcedResponse.php

 * The FormBuilder throws an EnforcedResponseException whenever a form
 * desires to explicitly set a response object. Exception handlers capable of
 * setting the response should extract the response object of such an exception
 * using EnforcedResponse::createFromException(). Then wrap it into an
 * EnforcedResponse object and replace the original response with the wrapped
 * response.

XB demo now has a webform component which renders fine, it submits fine but on submission, XB catches the EnforcedResponseException and that leads to failed rendering of the component:

Drupal\Core\Form\EnforcedResponseException occurred during rendering of component ded7bed0-4370-4186-bb97-2ec6490cf548 in Page Awesome Demo Experience Builder (1), field components:

Steps to reproduce:

1. Install xb-demo
2. Submit the webform. Check the resulting page and the logs.

Proposed resolution

Layout Builder rethrows this exception in Drupal\layout_builder\Plugin\Block\FieldBlock to allow for this flow to work.

User interface changes

πŸ› Bug report
Status

Active

Version

0.0

Component

… to be triaged

Created by

πŸ‡­πŸ‡ΊHungary GΓ‘bor Hojtsy Hungary

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @GΓ‘bor Hojtsy
  • πŸ‡­πŸ‡ΊHungary GΓ‘bor Hojtsy Hungary

    Demo video about the bug attached

  • πŸ‡§πŸ‡ͺBelgium wim leers Ghent πŸ‡§πŸ‡ͺπŸ‡ͺπŸ‡Ί

    Layout Builder rethrows this exception in Drupal\layout_builder\Plugin\Block\FieldBlock to allow for this flow to work.

    Indeed, but it points to a core issue too:

        // @todo Remove in https://www.drupal.org/project/drupal/issues/2367555.
        catch (EnforcedResponseException $e) {
          throw $e;
        }
    

    😭

  • πŸ‡§πŸ‡ͺBelgium wim leers Ghent πŸ‡§πŸ‡ͺπŸ‡ͺπŸ‡Ί

    it submits fine but on submission, XB catches the EnforcedResponseException and that leads to failed rendering of the component:

    This sounds related to πŸ“Œ ComponentSourceInterface::submitConfigurationForm and validateConfigurationForm are never called Active , but it's possible that just adding the catch when building block plugins is sufficient? πŸ€”

    Can you give this a try?

     .../ExperienceBuilder/ComponentSource/BlockComponent.php       | 10 +++++++++-
     1 file changed, 9 insertions(+), 1 deletion(-)
    
    diff --git a/src/Plugin/ExperienceBuilder/ComponentSource/BlockComponent.php b/src/Plugin/ExperienceBuilder/ComponentSource/BlockComponent.php
    index f0a7b094c..6921a97e2 100644
    --- a/src/Plugin/ExperienceBuilder/ComponentSource/BlockComponent.php
    +++ b/src/Plugin/ExperienceBuilder/ComponentSource/BlockComponent.php
    @@ -16,6 +16,7 @@ use Drupal\Core\Cache\CacheableMetadata;
     use Drupal\Core\Config\TypedConfigManagerInterface;
     use Drupal\Core\Entity\EntityInterface;
     use Drupal\Core\Entity\FieldableEntityInterface;
    +use Drupal\Core\Form\EnforcedResponseException;
     use Drupal\Core\Form\FormStateInterface;
     use Drupal\Core\Link;
     use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
    @@ -212,7 +213,14 @@ final class BlockComponent extends ComponentSourceBase implements ContainerFacto
           return $build;
         }
     
    -    $build['content'] = $block->build();
    +    try {
    +      $build['content'] = $block->build();
    +    }
    +    // @todo Remove in https://www.drupal.org/project/drupal/issues/2367555.
    +    catch (EnforcedResponseException $e) {
    +      throw $e;
    +    }
    +
         if (Element::isEmpty($build['content'])) {
           return $build;
         }
    
  • πŸ‡­πŸ‡ΊHungary GΓ‘bor Hojtsy Hungary

    I tried that, thanks, but that did not seem to change anything.

    From what I see src/Element/RenderSafeComponentContainer.php is where the logging comes from which catches all throwables:

      public function renderComponent(array $element): array {
        $context = new RenderContext();
        $element['#children'] = $this->renderer->executeInRenderContext($context, function () use (&$element, $context) {
          try {
            return $this->renderer->render($element['#component']);
          }
          catch (\Throwable $e) {
    

    So I think re-throwing it will still make it end up here and caught wholesale.

  • πŸ‡­πŸ‡ΊHungary GΓ‘bor Hojtsy Hungary
Production build 0.71.5 2024