Problem/Motivation

Using #lazy_builder and ['#cache']['keys'] together on the page level render array causes a

DomainException: When a #lazy_builder callback is specified, no properties can exist; all properties must be generated by the #lazy_builder callback. You specified the following properties: #cache_properties. in Drupal\Core\Render\Renderer->doRender() (line 331 of core/lib/Drupal/Core/Render/Renderer.php).

To reproduce:

  1. 1. Create a module "mymodule". The mymodule.routing.yml file should have the following contents:
    mycache.content:
      path: '/mycache'
      defaults:
        _controller: '\Drupal\mycache\Controller\MyCacheController::content'
        _title: 'My Cache'
      requirements:
        _permission: 'access content'
    
  2. 2. The src/Controller/MyCacheController.php file has:
    <?php
    /**
     * @file
     * Contains \Drupal\mycache\Controller\MyCacheController.
     *
     * @see https://www.drupal.org/developing/api/8/render/arrays/cacheability.
     */
    
    namespace Drupal\mycache\Controller;
    
    use Drupal\Core\Controller\ControllerBase;
    use Drupal\Core\Cache\Cache;
    
    class MyCacheController extends ControllerBase {
      public function content() {
        $renderer = \Drupal::service('renderer');
        $current_user = \Drupal::currentUser();
        $build = [
          '#lazy_builder' => [
            'Drupal\mycache\Controller\MyCacheController:mycache_helo', [$current_user->getAccountName()],
          ],
          '#create_placeholder' => TRUE,
          '#cache' => [
            'keys' => ['mycache'],
            'contexts' => ['user'],
          ],
        ];
        return $build;
      }
    
      public function mycache_helo($name) {
        return [
          '#markup' => t('Hi @name', ['@name' => $name]),
        ];
      }
    }
    
  3. Enable the module and go to /mycache.
  4. The above error is logged in dblog.

This issue happens only if [#cache]['keys'] is present. It is caused by Drupal\Core\Render\MainContent\HtmlRenderer::prepare() executing

$main_content['#cache_properties'][] = '#title';

and Drupal\Core\Render\Renderer::doRender() throwing

throw new \DomainException(sprintf('When a #lazy_builder callback is specified, no properties can exist; all properties must be generated by the #lazy_builder callback. You specified the following properties: %s.', implode(', ', $unsupported_keys)));
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

czigor created an issue. See original summary.

czigor’s picture

It seems that page content (the render array returned by the controller) should not be tried to be loaded via a #lazy_builder. The workaround I used was to move the whole array one level lower (into a top-level render array property).

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.6 was released on August 1, 2018 and is the final bugfix release for the Drupal 8.5.x series. Drupal 8.5.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.6.0 on September 5, 2018. (Drupal 8.6.0-rc1 is available for testing.)

Bug reports should be targeted against the 8.6.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.8.x-dev

Drupal 8.6.x will not receive any further development aside from security fixes. Bug reports should be targeted against the 8.8.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.9.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.7 was released on June 3, 2020 and is the final full bugfix release for the Drupal 8.8.x series. Drupal 8.8.x will not receive any further development aside from security fixes. Sites should prepare to update to Drupal 8.9.0 or Drupal 9.0.0 for ongoing support.

Bug reports should be targeted against the 8.9.x-dev branch from now on, and new development or disruptive changes should be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.2.x-dev

Drupal 8 is end-of-life as of November 17, 2021. There will not be further changes made to Drupal 8. Bugfixes are now made to the 9.3.x and higher branches only. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

JeroenT’s picture

Status: Active » Postponed (maintainer needs more info)
Issue tags: +Bug Smash Initiative

I tried to reproduce this issue. But I couldn't.

Are there any clear steps or some file I can use I can use to reproduce this error? Or even better, a failing test.

JeroenT’s picture

Status: Postponed (maintainer needs more info) » Needs work
FileSize
2.68 KB

Ok, So I was able to reproduce this error on Drupal 9.3 with the module I uploaded.

This no longer generates an exception, but is now a warning in the watchdog:

Warning: assert(): When a #lazy_builder callback is specified, no properties can exist; all properties must be generated by the #lazy_builder callback. You specified the following properties: #cache_properties. failed in Drupal\Core\Render\Renderer->doRender() (line 323 of /private/var/www/drupal_contribution/core/lib/Drupal/Core/Render/Renderer.php)

The stack trace:

#0 /private/var/www/drupal_contribution/core/includes/bootstrap.inc(346): _drupal_error_handler_real(2, 'assert(): When ...', '/private/var/ww...', 323)
#1 [internal function]: _drupal_error_handler(2, 'assert(): When ...', '/private/var/ww...', 323, Array)
#2 /private/var/www/drupal_contribution/core/lib/Drupal/Core/Render/Renderer.php(323): assert(false, 'When a #lazy_bu...')
#3 /private/var/www/drupal_contribution/core/lib/Drupal/Core/Render/Renderer.php(201): Drupal\Core\Render\Renderer->doRender(Array, false)
#4 /private/var/www/drupal_contribution/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php(241): Drupal\Core\Render\Renderer->render(Array, false)
#5 /private/var/www/drupal_contribution/core/lib/Drupal/Core/Render/Renderer.php(564): Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}()
#6 /private/var/www/drupal_contribution/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php(242): Drupal\Core\Render\Renderer->executeInRenderContext(Object(Drupal\Core\Render\RenderContext), Object(Closure))
#7 /private/var/www/drupal_contribution/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php(132): Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object(Symfony\Component\HttpFoundation\Request), Object(Drupal\Core\Routing\CurrentRouteMatch))
#8 /private/var/www/drupal_contribution/core/lib/Drupal/Core/EventSubscriber/MainContentViewSubscriber.php(90): Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object(Symfony\Component\HttpFoundation\Request), Object(Drupal\Core\Routing\CurrentRouteMatch))
#9 [internal function]: Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object(Symfony\Component\HttpKernel\Event\ViewEvent), 'kernel.view', Object(Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher))
#10 /private/var/www/drupal_contribution/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php(142): call_user_func(Array, Object(Symfony\Component\HttpKernel\Event\ViewEvent), 'kernel.view', Object(Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher))
#11 /private/var/www/drupal_contribution/vendor/symfony/http-kernel/HttpKernel.php(163): Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object(Symfony\Component\HttpKernel\Event\ViewEvent), 'kernel.view')
#12 /private/var/www/drupal_contribution/vendor/symfony/http-kernel/HttpKernel.php(80): Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object(Symfony\Component\HttpFoundation\Request), 1)
#13 /private/var/www/drupal_contribution/core/lib/Drupal/Core/StackMiddleware/Session.php(58): Symfony\Component\HttpKernel\HttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#14 /private/var/www/drupal_contribution/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(48): Drupal\Core\StackMiddleware\Session->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#15 /private/var/www/drupal_contribution/core/modules/page_cache/src/StackMiddleware/PageCache.php(191): Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#16 /private/var/www/drupal_contribution/core/modules/page_cache/src/StackMiddleware/PageCache.php(128): Drupal\page_cache\StackMiddleware\PageCache->fetch(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#17 /private/var/www/drupal_contribution/core/modules/page_cache/src/StackMiddleware/PageCache.php(82): Drupal\page_cache\StackMiddleware\PageCache->lookup(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#18 /private/var/www/drupal_contribution/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(48): Drupal\page_cache\StackMiddleware\PageCache->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#19 /private/var/www/drupal_contribution/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(51): Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#20 /private/var/www/drupal_contribution/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#21 /private/var/www/drupal_contribution/core/lib/Drupal/Core/DrupalKernel.php(708): Stack\StackedHttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#22 /private/var/www/drupal_contribution/index.php(19): Drupal\Core\DrupalKernel->handle(Object(Symfony\Component\HttpFoundation\Request))
JeroenT’s picture

Title: DomainException with #lazy_builder and ['#cache']['keys'] » #lazy_builder and ['#cache']['keys'] generates warnin assert(): When a #lazy_builder callback is specified, no properties can exist; all properties must be generated by the #lazy_builder callback. You specified the following properties: #cache_properties.

Version: 9.2.x-dev » 9.3.x-dev
Aron Novak’s picture

Status: Needs review » Needs work

The last submitted patch, 15: 2752889-cache_properties-14.patch, failed testing. View results

cmlara’s picture

I'm trying to understand what is this issue is asserting is wrong? As I understand #lazy_builder's are expected to be highly variable so it makes sense that providing keys when declaring the placeholder is forbidden as that context data is assumed to not be available at that time because only the #lazy_builder can know what it will work with, for example if it pulls a node it needs to get all the node's current key tags.

Generally #lazy_builder's place a unique string in the rendered page which is parsed by the render. This string links back to the #lazy_builder (with necessary serialized data).

It is my understanding the results of the #lazy_builder callback are stored in the DPC including any cache keys returned by the #lazy_builder allowing fast retrieval where possible and purging when data is adjusted. This is much like the results of a block being stored in the DPC, the #lazy_builder is essentially a distinct element in the page.

Is there something I am missing? In my opinion #2752889-2: #lazy_builder and ['#cache']['keys'] generates warnin assert(): When a #lazy_builder callback is specified, no properties can exist; all properties must be generated by the #lazy_builder callback. You specified the following properties: #cache_properties. appears to be the correct way to handler setting cache tags with #lazy_builder's making this issue appear to me to be Works as Designed.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.15 was released on June 1st, 2022 and is the final full bugfix release for the Drupal 9.3.x series. Drupal 9.3.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.4.x-dev branch from now on, and new development or disruptive changes should be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.9 was released on December 7, 2022 and is the final full bugfix release for the Drupal 9.4.x series. Drupal 9.4.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.5.x-dev branch from now on, and new development or disruptive changes should be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.