Problem/Motivation
I'm using field_group on one of my content types. Today I needed to add a new base field (boolean) on the same content type, and the field needs to be in the form display. I didn't want to set a weight, as I'm expecting Drupal will add one for me, by calculating the EntityFormDisplay::getHighestWeight().
There is an infinite loop in this logic when Drupal try to rebuild the form display - by visiting the content type Manage form display page with an empty cache - and field_group is part of the process.
I'm raising this a Critical because the Manage form display page becomes unresponsive.
Below the xdebug error stack:
Fatal error: Maximum function nesting level of '2048' reached, aborting!
[...]
19 Drupal\field_layout\Form\FieldLayoutEntityFormDisplayEditForm->getEntityFromRouteMatch( ) .../HtmlEntityFormController.php:73
20 Drupal\field_layout\Form\FieldLayoutEntityFormDisplayEditForm->getEntityDisplay( ) .../EntityDisplayFormBase.php:70
21 entity_get_form_display( ) .../EntityFormDisplayEditForm.php:56
22 Drupal\Core\Entity\Entity::load( ) .../entity.inc:558
23 Drupal\Core\Config\Entity\ConfigEntityStorage->load( ) .../Entity.php:524
24 Drupal\Core\Config\Entity\ConfigEntityStorage->loadMultiple( ) .../EntityStorageBase.php:212
25 Drupal\Core\Config\Entity\ConfigEntityStorage->doLoadMultiple( ) .../EntityStorageBase.php:242
26 Drupal\Core\Config\Entity\ConfigEntityStorage->mapFromStorageRecords( ) .../ConfigEntityStorage.php:189
27 Drupal\field_layout\Entity\FieldLayoutEntityFormDisplay->__construct( ) .../EntityStorageBase.php:322
28 Drupal\field_layout\Entity\FieldLayoutEntityFormDisplay->__construct( ) .../EntityFormDisplay.php:129
29 Drupal\field_layout\Entity\FieldLayoutEntityFormDisplay->init( ) .../EntityDisplayBase.php:143
30 Drupal\field_layout\Entity\FieldLayoutEntityFormDisplay->init( ) .../FieldLayoutEntityDisplayTrait.php:107
31 Drupal\field_layout\Entity\FieldLayoutEntityFormDisplay->setComponent( ) .../EntityDisplayBase.php:197
32 Drupal\field_layout\Entity\FieldLayoutEntityFormDisplay->getHighestWeight( ) .../EntityDisplayBase.php:365
33 Drupal\Core\Extension\ModuleHandler->invokeAll( ) .../EntityDisplayBase.php:409
34 call_user_func_array:{/var/www/html/web/core/lib/Drupal/Core/Extension/ModuleHandler.php:402} ( ) .../ModuleHandler.php:402
35 <strong>field_group_field_info_max_weight( ) </strong>.../ModuleHandler.php:402
36 <strong>field_group_info_groups( ) </strong>.../field_group.module:142
37 Drupal\Core\Entity\Entity::load( )
[...]
At this point the loop starts, field_group_info_groups()
has called EntityFormDisplay::load()
in 37, which is what actually started the process on 21/22.
How to reproduce:
- Add field_group to your content type (although by reading the code it looks like having field_group module enable is enough to trigger the bug.)
- Add a base field to your content type with
setDisplayConfigurable('form', TRUE)
, w/o specifing a 'weight' insetDisplayOptions()
- flush the cache
- Visit your content type Manage form display.
- You either see the blank page of dead, or any kind of php failure or - with xdebug enabled - "Fatal error: Maximum function nesting level of 'xxx' reached, aborting!"
Proposed resolution
I'm not sure what the solution can be in here. It looks like field_group is doing as expected, and core doesn't give much information from where to pull the weight on EntityFormDisplay::getHighestWeight()
other than from the display itself.
But if this is being rebuilt then an infinite loop is created.
Remaining tasks
Figure it out what to do, then fix it.
Comment | File | Size | Author |
---|---|---|---|
#13 | 2951175-13-recursion-check-1x.patch | 1.11 KB | dionsj |
#7 | 2951175-7-recursion-check.patch | 1.1 KB | charginghawk |
Comments
Comment #2
gambryAs mentioned in the issue summary, the only workaround is specifyng a 'weight' in
setDisplayOptions()
:Comment #3
seanrThis also fatals in a
drush cim -y
that enables field_group as part of the imported config when a module has defined other fields.Comment #4
gena.io CreditAttribution: gena.io commentedI've also got this error, and, if we disable xdebug from php.ini, we will receive "PHP Fatal error: Maximum execution time of %number seconds exceeded" with the following PHP crushing.
Comment #5
gena.io CreditAttribution: gena.io commentedComment #6
gena.io CreditAttribution: gena.io commentedFew ways how to fix it:
1) Check if your updates through update.php/drush updb have been finished correctly, especially if
system_post_update_extra_fields()
has executed. If it hasn't, try to re-run your updates and pay attention to errors if they appear.2) Check your
hook_entity_extra_field_info()
forweight
parameter existence. In my case, it was absent and caused the mentioned bug. Yourhook_entity_extra_field_info()
should look like this:Hope it will help you.
Comment #7
charginghawk CreditAttribution: charginghawk as a volunteer commentedI ran into this issue in a roundabout way - while saving a node with the menu settings populated, the new menu item was run through Lingotek, which loaded view display and triggered field_group_field_info_max_weight() and set off the infinite loop (and subsequently caused any node save to fail if that field was populated). I've got a patch that checks for recursion and shuts it down based on similar work here:
https://www.drupal.org/project/drupal/issues/2966137
Comment #9
charginghawk CreditAttribution: charginghawk as a volunteer commentedMarking needs review since the failure was from the branch, not the patch.
Comment #10
joachim CreditAttribution: joachim commentedI'm not sure this is the right way to fix this.
From what I can tell, the problem happens because of this:
- 1. when an entity display config entity (ie view or form configuration) is loaded, it sets up all of its components.
- 2. if a component doesn't specify a weight, then it puts it at the bottom:
3. getHighestWeight() checks all the components' weights. As part of that, it invokes hook_field_info_max_weight() to let other modules act.
4. field_group's implementation of this, field_group_field_info_max_weight(), calls field_group_info_groups() to load all the groups
5. in order to load the groups, it loads up the display entity, EntityFormDisplay::load/EntityViewDisplay::load
6. and BOOM we're back at step 1
I think the problem is that field_group_field_info_max_weight() needs the display config entity in order to act. Arguably, hook_field_info_max_weight() should be providing it as a parameter -- which would be something to fix in core.
Comment #11
charginghawk CreditAttribution: charginghawk as a volunteer commentedIMO, infinite recursion under any circumstances isn't acceptable, and this fixes that in a definite way without losing anything. It'd just be cruft if a theoretical core solution comes along, but in that case, we'll be updating this code anyway and can axe it then. In the meantime, this solves a critical issue for a heavily used module. That said, the patch is there for anyone who wants it.
Comment #12
gambryI see @joachim , I believe Core should either suggest a solution or at least alert developers. But doing this could seriously take time... and core committers may also argue this is something
field_group
should take care of, and in that case we just wasted time.I'd +1 #11 as this is critical, however currently
field_group_field_info_max_weight
is returning NULL when a recursion is triggered. This is not ideal if actually there is a field_group with a valid weight?Why instead of returning NULL don't we read directly from the config entity?
Comment #13
dionsjI've encountered this issue on version 1.x of field_group, I made the same changes as the patch in #7, but for version 1.x.
It solves the problem for me.
I've attached the patch for anyone who is also still using 1.x
Comment #14
super_romeo CreditAttribution: super_romeo as a volunteer commented@gambry's question from #12 is important.
Comment #15
swentel CreditAttribution: swentel at eps & kaas for Dropsolid commentedThat was my initial thought as well, but not sure whether that is worth the effort. Will talk with alex whether if that's a good idea or not.
Comment #16
swentel CreditAttribution: swentel at eps & kaas for Dropsolid commentedTalked with some other people and this approach is fine, so will commit and push. Made a tiny change in the comments: used 'entity display' instead of 'entity' alone.
This is actually fine, NULL is returned for the second time this hooks comes in, but that doesn't matter. When it can finally go through, it returns the max weight fine - unless I saw it wrong in my debugger, but even then I don't mind /that/ much.
Comment #18
swentel CreditAttribution: swentel at eps & kaas for Dropsolid commentedcommited and pushed, thanks!
pushed to 8.x-1.x as well
Comment #21
adam-delaney CreditAttribution: adam-delaney at Principal Financial Group commentedI have recently encountered this issue with the update to Drupal 9.4.0 with a base field that does not specify weight in it's form display.
Comment #22
moshe weitzman CreditAttribution: moshe weitzman commentedI'm seeing same as Adam Delaney. The workaround is to specify the weight.
For some reason $recursion_check is not working as a static variable should. Its always an empty array even on subsequent calls to field_group_field_info_max_weight().
Comment #23
sjerdoI'm experiencing the same issue as adam-delaney and moshe weitzman in Drupal 9.4.3. Adding a weight fixes the issue for me (as a workaround).
Comment #24
gambry@adam-delaney @moshe weitzman @sjerdo what version of the module do you use?
I noticed 1.x uses
static
keyword, while 3.x usesdrupal_static
.Also if you get debug further and post more details that would be useful.
Comment #25
sjerdo@gambry We are using field_group version 8.x-3.2 and Drupal Core 9.4.3
The error occurs on the ContentEntityForm of a custom content entity type (@ContentEntityType)
in function baseFieldDefinitions we changed the following field definition:
and added a weight to 'fix' the issue:
Comment #26
donquixote CreditAttribution: donquixote at European Commission and European Union Institutions, Agencies and Bodies commentedActually another perhaps "cleaner" solution would be if `field_group_info_groups()` would not load the complete view mode / form mode, but just the raw config data. But I don't know if that is possible or would cause other problems.
Comment #27
donquixote CreditAttribution: donquixote at European Commission and European Union Institutions, Agencies and Bodies commentedActually the entire mechanism and hook in core are quite problematic and seem to be not very useful.
The only implementation I could find is from field group.
And I wonder if it ever does anything useful. I would think that it runs into the infinite recursion.