SetEcaLogLevel cannot be instantiated when eca.settings is absent, so the action vanishes from the action list
Problem/Motivation
SetEcaLogLevel declares a non-nullable, uninitialized typed property and
populates it unconditionally from configuration:
modules/base/src/Plugin/Action/SetEcaLogLevel.php:43
protected int $configuredLogLevel;modules/base/src/Plugin/Action/SetEcaLogLevel.php:50
$instance->configuredLogLevel = $container->get('config.factory')->get('eca.settings')->get('log_level');When eca.settings is absent, or present without log_level, get() returns
NULL and the assignment raises:
TypeError: Cannot assign null to property Drupal\eca_base\Plugin\Action\SetEcaLogLevel::$configuredLogLevel of type intActions::createInstance() (src/Service/Actions.php:134-150) catches
\Exception | \Throwable, logs an error, and returns NULL. Actions::actions()
then simply skips the plugin (src/Service/Actions.php:113).
The net effect is that the action disappears from the available-actions list rather than failing loudly. A watchdog error is written, so it is not entirely silent — but nothing at the point of use indicates why the action is missing, and in a kernel test the log is easy to miss entirely.
Because the property is also read in
execute() (:61) and cleanupAfterSuccessors() (:73), an uninitialized
property would additionally raise Error: Typed property ... must not be accessed before initialization if an instance ever reached those paths without
create() having succeeded.
Steps to reproduce
- In a kernel test, install the
eca_basemodule withoutinstallConfig(['eca']). - Ask
eca.service.actionfor the available actions (->actions()).
Result: eca_set_eca_log_level is not in the list; a watchdog error records
the TypeError.
Expected: either the action is available with a sensible fallback log level,
or the failure is surfaced where a developer will see it.
The same shape applies to any site or test fixture where eca.settings has not
been installed.
Proposed resolution
Make the plugin resilient to absent configuration. Options, roughly in increasing order of invasiveness:
- Type the property as
?int(or give it a default) and coerce the read, e.g. fall back to a documented default log level when the setting is missing. - Read the configuration lazily at the point of use rather than in
create(), so constructing the plugin cannot fail. - Separately, consider whether
Actions::createInstance()swallowing\Throwableis the right default. It keeps one broken plugin from taking down the whole action list, which is valuable — but aTypeErrorin a plugin's own constructor is a coding error rather than an environmental one, and converting it into "this action does not exist" makes it hard to find. At minimum the error message could distinguish the two.
Options 1 and 2 are local and low-risk. Option 3 is a broader behavioral question and could be split out.
Remaining tasks
- Make
SetEcaLogLevelconstructible withouteca.settings - Decide on a documented fallback log level
- Add test coverage instantiating the action with
eca.settingsabsent - Optionally revisit the
Throwablehandling inActions::createInstance()
User interface changes
None, other than the action no longer vanishing from the action list.
Data model changes
None.
Found while backporting #3590375 (closed) to 3.0.x: this defect hid
eca_set_eca_log_level.log_level from the runtime plugin enumeration used by
that issue's guard test, which is why the key was invisible to a
runtime-based audit. The schema itself is correct on both branches.
AI-Generated: Yes (Used OpenCode to investigate why a plugin was missing from runtime enumeration while backporting #3590375 (closed). All file and line references were verified against the 3.1.x source.)