-
Notifications
You must be signed in to change notification settings - Fork 682
Description
What happened?
Bug Report: disclosuremenu.twig crashes on non-iterable menu items
Note: I've asked Copilot to create this bug report based on our findings. Sadly it's very hard to reproduce the issue as it seems to only occur in a situation very specific to the website we're currently upgrading from 4 to 5, and we haven't been able to pinpoint what exactly sets this site apart from all the other ones that we've upgraded in the past. I can confirm that the fix proposed by Copilot resolves the issue.
Summary
The disclosuremenu.twig template in Craft CMS 5.9.x throws an error when menu items contain non-iterable values (such as Color enum objects or strings). This prevents users from opening entries in the Control Panel.
Error Message
yii\base\ErrorException: Undefined property: craft\enums\Color::$selected
in /vendor/yiisoft/yii2/helpers/BaseArrayHelper.php:221
Environment
- Craft CMS Version: 5.9.3
- PHP Version: 8.2
- Craft Commerce: 5.5.2
- Upgraded from: Craft 4
Steps to Reproduce
- Upgrade a Craft 4 site to Craft 5.9.3
- Have multiple sections/entry types configured
- Open any entry in the Control Panel
- Error occurs when rendering the CP layout
Note: This bug is difficult to reproduce on fresh installations. It appears to be triggered by specific data configurations that may occur during migration or with sites that have many sections.
Root Cause Analysis
The bug is located in vendor/craftcms/cms/src/templates/_includes/disclosuremenu.twig, line 7:
{% set hasSelected = items|map(i => (i.items ?? [i]))|flatten(1)|contains('selected') %}Problem
The |contains('selected') filter calls ArrayHelper::contains(), which iterates through all flattened items and attempts to access a selected property on each one. When non-iterable values are present in the flattened array (such as Color enum objects or strings like "link"), the property access fails.
How non-iterable values end up in the array
-
Color enum objects: Entry types can have a
colorproperty set. When this is passed through to disclosure menus via theiconColorproperty, theColorenum object ends up in the items array.From
vendor/craftcms/cms/src/elements/Entry.php(around line 2562):'iconColor' => $et->color, // This is a Color enum when set
-
String values: Menu item properties like
type: 'link'can leak into the flattened array under certain data configurations.
Stack Trace
#0 vendor/craftcms/cms/src/web/ErrorHandler.php(115)
#1 vendor/yiisoft/yii2/helpers/BaseArrayHelper.php(221): accessing ->selected on Color enum
#2 vendor/craftcms/cms/src/helpers/ArrayHelper.php(494): getValue(Color, 'selected', NULL)
#3 vendor/craftcms/cms/src/helpers/ArrayHelper.php(277): contains(Array, 'selected')
#4 storage/runtime/compiled_templates/.../disclosuremenu.twig(57)
...
The error also manifests in breadcrumb menus (crumbs.twig:54) where string values like "link" appear in the flattened items.
Proposed Fix
Add a filter to exclude non-iterable values before calling |contains():
Before (buggy):
{% set hasSelected = items|map(i => (i.items ?? [i]))|flatten(1)|contains('selected') %}After (fixed):
{% set hasSelected = items|map(i => (i.items ?? [i]))|flatten(1)|filter(i => i is iterable)|contains('selected') %}This ensures only array/object items that can have a selected property are checked.
Workaround
Until an official fix is released, users can create a module that patches the vendor file on load:
<?php
namespace modules\craft5compat;
use Craft;
use yii\base\Module;
class Craft5Compat extends Module
{
public function init(): void
{
parent::init();
$this->applyDisclosureMenuPatch();
}
private function applyDisclosureMenuPatch(): void
{
$vendorTemplatePath = Craft::getAlias('@vendor/craftcms/cms/src/templates/_includes/disclosuremenu.twig');
if (!file_exists($vendorTemplatePath)) {
return;
}
$content = file_get_contents($vendorTemplatePath);
$buggyLine = "{% set hasSelected = items|map(i => (i.items ?? [i]))|flatten(1)|contains('selected') %}";
$fixedLine = "{% set hasSelected = items|map(i => (i.items ?? [i]))|flatten(1)|filter(i => i is iterable)|contains('selected') %}";
if (strpos($content, $buggyLine) !== false) {
$patchedContent = str_replace($buggyLine, $fixedLine, $content);
file_put_contents($vendorTemplatePath, $patchedContent);
}
}
}Additional Context
- The issue was confirmed on a site upgraded from Craft 4 with 31+ sections
- All plugins were disabled during testing - the issue persists with only core Craft
- Fresh Craft 5 installations with similar configurations do not exhibit this issue
- The bug may be related to how data structures are transformed during Craft 4 → 5 migration
Related Files
vendor/craftcms/cms/src/templates/_includes/disclosuremenu.twig(line 7)vendor/craftcms/cms/src/templates/_layouts/components/crumbs.twig(line 54)vendor/craftcms/cms/src/helpers/ArrayHelper.php(contains method)vendor/craftcms/cms/src/elements/Entry.php(iconColor property)
Craft CMS version
Craft Pro 5.9.3 (also occurs in earlier versions)
PHP version
8.2.29
Operating system and version
Linux 6.6.87.2-microsoft-standard-WSL2
Database type and version
MariaDB 10.8.8
Image driver and version
Imagick 3.8.0 (ImageMagick 6.9.11-60)
Installed plugins and versions
All plugins were disabled during testing, including Craft Commerce, but the problem persisted. Here's a list of installed plugins, all except for site-copy-x were enabled initially:
panlatent/craft-aliyun 1.1.2
craftcms/aws-s3 2.2.4
born05/craft-assetusage 4.1.3
craftcms/ckeditor 4.11.0
craftcms/commerce 5.5.2
vaersaagod/geomate 3.1.0
verbb/hyper 2.3.0
nystudio107/craft-minify 5.0.0
spicyweb/craft-neo 5.5.4
ttempleton/craft-nocache 3.0.4
craftcms/postmark 3.1.0
ostark/craft-relax 2.1.0
nystudio107/craft-retour 5.0.13
nystudio107/craft-seomatic 5.1.20
nst/craft-sitecopy 2.1.1
craftcms/store-hours 4.2.0
verbb/super-table 4.0.5
verbb/wishlist 3.0.12