What happened?
I experienced this issue while applying a Craft update in a project's develop branch after having created sections and fields in a feature branch (which, of course, don't exist in the develop branch's config). Claude Code found out the cause, and so I had it generate a bug report, a modified version of which is below. Thanks.
Also see #12163, #16150, #15787, #19031.
Description
Running php craft project-config/apply --force throws a fatal yii\base\ErrorException: Trying to access array offset on null at src/services/Entries.php:1675 when the config diff includes the deletion of an entry type, and the deletion cascades such that the entryTypes.<uid> path resolves to null in both the existing and incoming config.
The crash only occurs with --force. Without it, the same apply completes cleanly.
Root cause
--force sets ProjectConfig::$forceUpdate = true (src/console/controllers/ProjectConfigController.php:314).
In ProjectConfigData::commitChanges(), forceUpdate unconditionally forces $valueChanged = true (src/models/ProjectConfigData.php:64-68), even when $oldValue === null && $newValue === null (which happens for an entry-type path whose parent removal has already been processed earlier in the same apply run).
The event-dispatch block then can't classify it as add or remove, so it falls into the else branch and fires EVENT_UPDATE_ITEM (src/models/ProjectConfigData.php:74-86):
if ($newValue === null && $oldValue !== null) {
// removeItem
} elseif ($oldValue === null && $newValue !== null) {
// addItem
} else {
// updateItem <-- both null lands here
$this->projectConfig->trigger(ProjectConfigService::EVENT_UPDATE_ITEM, $event);
}
That routes to Entries::handleChangedEntryType() (the add/update handler), which immediately dereferences the null newValue:
// src/services/Entries.php:1675
$entryTypeRecord->name = $data['name']; // $data = $event->newValue = null
An entry-type removal should never reach handleChangedEntryType(); a null → null path should not fire updateItem at all.
Stack trace (abridged)
yii\base\ErrorException: Trying to access array offset on null
in src/services/Entries.php:1675
#1 Entries->handleChangedEntryType()
#2 ProjectConfig->handleChangeEvent() (EVENT_UPDATE_ITEM)
#5 ProjectConfigData->commitChanges(oldValue: NULL, newValue: NULL, path: 'entryTypes.<uid>', triggerUpdate: FALSE, force: TRUE)
#8 ProjectConfig->_applyChanges(['newItems' => ...])
#9 ProjectConfigController->actionApply()
Suggested fix
Guard the else branch (or handleChangedEntryType) against a null newValue, and/or skip event dispatch when $oldValue === null && $newValue === null even under forceUpdate.
Steps to reproduce
- Have an entry type present in the loaded/DB project config but absent from the config/project/ YAML (i.e. a pending deletion).
- Run
php craft project-config/apply --force.
- During apply, the entry type's parent path is removed first; a later
entryTypes.<uid> path then commits with both old and new value null.
Expected behavior
The entry type is deleted (via handleDeletedEntryType) and apply completes — as it does without --force.
Actual behavior
Fatal Trying to access array offset on null; apply aborts partway, leaving project config in an inconsistent, partially-applied state.
Craft CMS version
5.10.7
PHP version
8.3
Operating system and version
NixOS 26.05
Database type and version
MySQL 8.0
Image driver and version
No response
Installed plugins and versions
No response
What happened?
I experienced this issue while applying a Craft update in a project's develop branch after having created sections and fields in a feature branch (which, of course, don't exist in the develop branch's config). Claude Code found out the cause, and so I had it generate a bug report, a modified version of which is below. Thanks.
Also see #12163, #16150, #15787, #19031.
Description
Running php craft
project-config/apply --forcethrows a fatalyii\base\ErrorException: Trying to access array offset on nullatsrc/services/Entries.php:1675when the config diff includes the deletion of an entry type, and the deletion cascades such that theentryTypes.<uid>path resolves tonullin both the existing and incoming config.The crash only occurs with
--force. Without it, the same apply completes cleanly.Root cause
--forcesetsProjectConfig::$forceUpdate = true(src/console/controllers/ProjectConfigController.php:314).In
ProjectConfigData::commitChanges(),forceUpdateunconditionally forces$valueChanged = true(src/models/ProjectConfigData.php:64-68), even when$oldValue === null && $newValue === null(which happens for an entry-type path whose parent removal has already been processed earlier in the same apply run).The event-dispatch block then can't classify it as add or remove, so it falls into the else branch and fires
EVENT_UPDATE_ITEM(src/models/ProjectConfigData.php:74-86):That routes to
Entries::handleChangedEntryType()(the add/update handler), which immediately dereferences the null newValue:An entry-type removal should never reach
handleChangedEntryType(); a null → null path should not fireupdateItemat all.Stack trace (abridged)
Suggested fix
Guard the else branch (or
handleChangedEntryType) against anullnewValue, and/or skip event dispatch when$oldValue === null && $newValue === nulleven underforceUpdate.Steps to reproduce
php craft project-config/apply --force.entryTypes.<uid>path then commits with both old and new value null.Expected behavior
The entry type is deleted (via
handleDeletedEntryType) and apply completes — as it does without--force.Actual behavior
Fatal Trying to access array offset on null; apply aborts partway, leaving project config in an inconsistent, partially-applied state.
Craft CMS version
5.10.7
PHP version
8.3
Operating system and version
NixOS 26.05
Database type and version
MySQL 8.0
Image driver and version
No response
Installed plugins and versions
No response