Problem/Motivation

After renaming a layout, or after switching to a branch where a given layout does not exist, any attempt to fix the layout configuration will fail.

Steps to reproduce

Create a custom layout.
Create a layout builder configuration using that custom layout.
Remove or rename the custom layout.
Attempt to edit the layout builder configuration form, OR try to use "drush cim" to revert to a configuration where this layout was not used.

Expected: Layout builder config can be repaired.
Actual: Error.

The "layout_xyz" plugin does not exist. Valid plugin IDs for Drupal\Core\Layout\LayoutPluginManager are: ...

Proposed resolution

Use try/catch in strategic places to recover from this.

Remaining tasks

See tags

User interface changes

API changes

Data model changes

Release notes snippet

Issue fork drupal-3204271

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

donquixote created an issue. See original summary.

donquixote’s picture

Places where we could insert try/catch:

core/modules/layout_builder/src/Section.php, function getDefaultRegion()
the exception occurs in $this->layoutPluginManager()->getDefinition($this->getLayoutId()), if the layout is missing.

core/modules/layout_builder/src/Section.php, function getLayout()
the exception occurs in $this->layoutPluginManager()->createInstance($this->getLayoutId(), $this->layoutSettings), if the layout is missing.

core/lib/Drupal/Core/Entity/EntityDisplayBase.php, function init()
the exception occurs in $default_region = $this->getDefaultRegion(), if the layout for the first section is missing.

Adding a try/catch is easy, but we need to define a sensible fallback behavior that works in all cases where the layout is used:
- in the view mode or layout builder admin form.
- in the front-end, when displaying the layout.
- when importing or exporting settings.
- other?

donquixote’s picture

Status: Active » Needs review
StatusFileSize
new1.65 KB

Proof of concept patch.
Perhaps there are other implications to this, we should be careful before we commit.

donquixote’s picture

Btw about the @throws tag:
PhpStorm will show an inspection warning "Redundant @throws tag".
I started a discussion here:
https://youtrack.jetbrains.com/issue/WI-59821

tim.plunkett’s picture

I think this would be better handled directly in LayoutBuilderEntityViewDisplay::getDefaultRegion()

Or even hasSection()...

Layout Builder can break in a lot of different ways if you remove the plugin from your system while still using it (though that's true of most things in PHP? I don't know that we want to babysit this too much...)

donquixote’s picture

I think this would be better handled directly in LayoutBuilderEntityViewDisplay::getDefaultRegion()

Or even hasSection()...

But these functions cannot produce a reasonable fallback value, if the layout is missing.

We could return NULL or 'content', but this would just produce wrong behavior down the line.

I think it is fine to throw exceptions if something is broken, and no clean fallback behavior can be provided.

Layout Builder can break in a lot of different ways if you remove the plugin from your system while still using it (though that's true of most things in PHP? I don't know that we want to babysit this too much...)

I only want to fix the case where we deploy a new version of a website with new layouts and new config, so that a "drush cim" does not break. The site itself can remain broken until the configuration is fixed.

Ideally the display object should be put into a "failed init" state, where rendering will fail, but overwriting the config still works.

In an ideal world, the raw config should be handled by a different object or layer than the rendering, so that the config could be updated even if the layout does not exist. But this would be a lot more work than this try/catch.

donquixote’s picture

The site itself can remain broken until the configuration is fixed.

To clarify:

We should catch and handle exceptions in strategic places where we can provide a reasonable fallback behavior and logging / reporting capability.

A fallback behavior when trying to display a broken, missing or misconfigured layout could be to display nothing, or to display all the elements in a single region. And log the exception, of course.

When trying to edit broken layout configuration in the layout builder UI, a fallback behavior is to show all the form elements in a default state, and show a message: "The stored configuration is broken with the currently available layouts, and saving this form will wipe it."

When trying to overwrite broken config with drush cim, I think it is fine to simply wipe the broken config.

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

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

herved’s picture

Patch #3 works as expected for me and solves the config:import issue on deployment after removing a layout that is now unused.
I can't see any issues so far.
So +1 on this! Very useful, thanks.

tim.plunkett’s picture

Status: Needs review » Needs work
Issue tags: +Blocks-Layouts, +Needs tests

Regardless of implementation, this needs automated tests that follow the "steps to reproduce".

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

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.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.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now 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.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now 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.

seanb’s picture

Status: Needs work » Needs review
StatusFileSize
new1.42 KB

Just ran into this. Attached patch might be a subtle approach to not break when a layout is removed. Thoughts?

seanb’s picture

StatusFileSize
new1.18 KB
new2.41 KB

Sorry, ran into an error while saving as well, I guess the checks need to be just a bit more.

seanb’s picture

StatusFileSize
new2.39 KB

Reroll for 10.1.x

smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Needs review, +Needs issue summary update

This issue is being reviewed by the kind folks in Slack, #need-reveiw-queue. We are working to keep the size of Needs Review queue [2700+ issues] to around 200, following Review a patch or merge require as a guide.

Moving back to NW for the test cases

Also tagging for IS update for the proposed solution and if any steps are different from D8 when this was reported.

quietone’s picture

Issue summary: View changes
Issue tags: -Needs review

Removing 'needs review' tag.

Version: 10.1.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, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

alex.skrypnyk’s picture

StatusFileSize
new616 bytes

This is potentially related to https://www.drupal.org/project/drupal/issues/2860346

The definitions are cached during a theme install.

If a theme provides custom layouts and has configuration objects that use those layouts (like `entity_view_display`), the layouts plugin definitions will already be cached and newly installed layouts will not have a chance to be discovered during the process of the theme installation.

Adding a cache tag that depends on installed extensions resolve this issue.

In LayoutPluginManager:
$this->setCacheBackend($cache_backend, $type, ['config:core.extension']);

Patch attached.

herved’s picture

For me #20 doesn't work.
I have a theme that is already installed, which declares some layouts, and some configs using those layouts (in config sync).
When I change a layout machine name and attempt to clear cache and config import, it fails.

#16 and #3 are working but a clear cache is required it seems before config import.

boregar’s picture

#20 worked for me on a fresh install with CivicTheme 1.6.2 on Drupal Core 10.2.3. Thanks!

kristen pol’s picture

I used patch #20 on Drupal 10.3.0 and CivicTheme 1.7.1 and the fatal error went away.

kristen pol’s picture

Issue summary: View changes

It would be great if someone wanted to review the different approach in #20, update the issue summary, and add tests :)

joshua1234511 made their first commit to this issue’s fork.

anjaliprasannan made their first commit to this issue’s fork.

anjaliprasannan’s picture

@joshua1234511 The test is failing in the pipeline. I tried it in the local by following the test steps where I could not find a message as in the test case. Is the test written as per requirement? Should the test be rewrorked on?

joshua1234511’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests, -Needs issue summary update +Needs manual testing

Reverted the added test. Mock tests are complex to integrate into the layout build scenario for scenario-based execution. Manual testing is required for this fix.

smustgrave’s picture

Going to disagree that we should have test coverage added but will leave in review.

danielveza’s picture

This absolutely needs test coverage.

I haven't thoroughly looked into this issue yet, would it work to have a test module that provides a layout, add that layout to a page, then uninstall that module and verify everything still works as expected? If I get a chance I'll look into this.

smustgrave’s picture

Status: Needs review » Needs work

For the tests

danielveza’s picture

Issue tags: -Needs tests

I've added test coverage, and also fixed issues where this wasn't working on the default layout or on nodes that did not have overridden layouts (Yay for tests)

Lets see how the pipeline goes here. I'm a little iffy of how many places we are calling ::hasLayout. I wonder if we can put that in a central place instead.

danielveza’s picture

Status: Needs work » Needs review

Test coverage added and tests are green. I'd be interested to get a second set up eyes on this one. Mainly around this comment.

I'm a little iffy of how many places we are calling ::hasLayout. I wonder if we can put that in a central place instead.

solideogloria’s picture

I get this error when installing the new Navigation module. (I'm not using any patches for this issue, just commenting that this is a way the issue occurs.) The error occurs for every single route I visit.

Drupal\Component\Plugin\Exception\PluginNotFoundException: The "navigation_layout" plugin does not exist.

This issue is prohibiting me from converting my site to the new module. I'm currently on Drupal 10.4

solideogloria’s picture

I applied the MR diff as a patch to my Drupal 10.4 site, and I still get the error.

smustgrave’s picture

Status: Needs review » Needs work

Per #36

solideogloria’s picture

Let me know if you need me to provide a stack trace or anything else.

grimreaper made their first commit to this issue’s fork.

grimreaper’s picture

StatusFileSize
new28.95 KB

Hi,

I made an addition in getLayoutSettings to avoid it to fail if the layout plugin does not exist.

This is helpful in case of update like for UI Patterns 1 to UI Patterns 2 upgrade.

Attaching patch for Composer usage.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.

herved’s picture

StatusFileSize
new12.72 KB

MR rebased, attaching current snapshot.

Some performance tests are failing and affected by the added cache tag in LayoutPluginManager.
I tried to reproduce the issue mentioned in #20 and #35 but without success.
Could you please provide steps to reproduce?

I tried this in LayoutBuilderMissingLayoutTest, but it passes without the added cache tag.

  /**
   * Tests that installing the Navigation module does not cause a plugin error.
   */
  public function testInstallingNavigationModuleDoesNotBreakLayouts(): void {
    // Warm the layout definitions cache.
    /** @var \Drupal\Core\Layout\LayoutPluginManagerInterface $manager */
    $manager = \Drupal::service(LayoutPluginManagerInterface::class);
    $definitions = $manager->getDefinitions();
    $this->assertArrayNotHasKey('navigation_layout', $definitions);

    \Drupal::service('module_installer')->install(['navigation']);

    $user = $this->drupalCreateUser(admin: TRUE);
    $this->drupalLogin($user);
    $this->drupalGet('<front>');
    $this->assertSession()->statusCodeEquals(200);

    // Verify the layout is now properly discovered.
    $manager = \Drupal::service(LayoutPluginManagerInterface::class);
    $this->assertTrue($manager->hasDefinition('navigation_layout'));
  }