Problem/Motivation

Currently, when a plugin cannot be found, we get this obtuse error message:

Drupal\Component\Plugin\Exception\PluginException: The plugin (%plugin) did not specify an instance class in Drupal\Component\Plugin\Factory\DefaultFactory->getPluginClass() (line 60 of /path/to/webroot/core/lib/Drupal/Component/Plugin/Factory/DefaultFactory.php).

first, this is incorrect - what's usually happening is that no plugin definition was found, not that the found plugin definition did not specify an instance class. second, it gives us very little useful information about WHICH plugin actually failed - we just have the plugin id, not the owner/type information. that's a little tricky to get at.

Proposed resolution

introduce new exceptions - PluginNotFoundException and DerivativeNotFoundException - that can be thrown by the various Discovery implementations when plugins aren't actually found.

Remaining tasks

the exceptions still don't include contextual information about the plugin type when throwing exceptions. we could probably expand the DiscoveryInterface to include such contextual information purely for the purpose of these exceptions.

API changes

any new Discovery classes, decorators, etc., should follow the practice of throwing the appropriate exception for their case.

Comments

sdboyer’s picture

StatusFileSize
new11.15 KB

Initial patch.

sdboyer’s picture

Status: Active » Needs review
sdboyer’s picture

we should really differentiate the messages in the exceptions, especially for some of the decorators.

Status: Needs review » Needs work

The last submitted patch, discovery-exceptions-1846070-1.patch, failed testing.

neclimdul’s picture

I agree with the things said here.

sdboyer’s picture

Title: Improve exceptions thrown when plugins cannot be found » Improve exception messages when plugins cannot be found
Status: Needs work » Needs review
StatusFileSize
new21.57 KB

ok, this should go green. i've made the following changes:

  • changed the signature of DiscoveryInterface::getDefinition() to have a second parameter, a boolean indicating whether or not an exception should be thrown on failure. this allows plugin managers that want to expect failure in the discovery process to avoid having to use exceptions at runtime to provide default/fallbacks. i'm not really sure i like this - i think i'd rather the default selection happen within discovery more easily, somehow - but this was a reasonable stopgap i discussed with yched after solving some of the field[_ui]-related failures in the initial approach.
  • PluginManagerBase::getDefinition() now has its own try/catch block in it, wherein it waits for one of these PluginNotFoundExceptions, catches it, then throws a new one but includes the current class name in it. this is the simplest thing i could come up with to provide more context than simply the plugin id..however, still less than optimal.
  • updated the various plugin system tests to expect and test for these two new exceptions.

i would really like a review from yched on this to see if there's a way we could make the interaction of formatters & widgets with this new system a bit more elegant.

Status: Needs review » Needs work

The last submitted patch, discovery-exceptions-1846070-6.patch, failed testing.

sdboyer’s picture

Status: Needs work » Needs review
StatusFileSize
new21.86 KB

pheh. silly use statement conflict.

yched’s picture

So yeah, as discussed on IRC, we do have valid runtimes cases where we want to be able to query a non-existing plugin_id - like "does this plugin exist ?".
That's what field_info_widget_types($widget_plugin_id) does, and it currently returns NULL - switching that to "throws an exception woudl be a non minor API change".
Also, WidgetPluginManager::getInstance() checks whether the requested plugin_id exists and switches back to the default widget if not.
For all those cases, we agreed that we don't really want to bring "throw exceptions / catch them and handle" to become a regular pattern in "normal" runtime execution.

But as sdboyer points, for other cases - notably when the retrieved definition is passed to a factory, passing NULL only causes cryptic exceptions down the line, and raising an exception at the origin of the error would make much easier debugging.

So we need a way to say how we want getDefinition() to behave - exception or NULL.
Actually Symfony's DIC has something like this this already :

interface ContainerInterface
{
    const EXCEPTION_ON_INVALID_REFERENCE = 1;
    const NULL_ON_INVALID_REFERENCE      = 2;
    const IGNORE_ON_INVALID_REFERENCE    = 3;

    public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE);
}

...
$foo = $container->get('bar', ContainerInterface::NULL_ON_INVALID_REFERENCE):
...

(also used for new Reference('bar', ContainerInterface::NULL_ON_INVALID_REFERENCE); on ContainerBuilders, so that's a pattern that can potentially be found in our Bundle::build() implementations already)

So, while I approve the approach, I wonder whether, rather than a bool whose signification is never too clear when looking at the calling code, we should go with similar constants (although I couldn't really make sense of IGNORE_ON_INVALID_REFERENCE).

Also, whether bool on constants, having to reproduce the supporting logic in all discoveries and decorators kind of begs for #1809200: Consolidate discovery code in a DiscoveryBase and DiscoveryDecoratorBase :-)

Status: Needs review » Needs work

The last submitted patch, discovery-exceptions-1846070-8.patch, failed testing.

sdboyer’s picture

yeah, the named params is a much nicer way of doing this. switching over to that.

agreed that all the redundant code begs for a consolidated approach.

i think part of the reason this code makes me twitchy is because the invalid behavior param doesn't really pass down through the decorated layers. each decorator calls the getDefinitions() method of what it decorates, not getDefinition(), which makes the contract suggested by the parameter somewhat brittle and reliant on other wrapping code making sure to respect it. not a critical concern, but a touch concerning.

i still feel that the inclusion of a parameter to allow specifically for this defaulting use case smells of bad architecture. the fact that we need to think about that at all is a product of the fact that we've been abusing decorators like we have. really, these exceptions are more intended for client code outside of the plugin system.

sdboyer’s picture

Status: Needs work » Needs review
StatusFileSize
new24.56 KB
new13.67 KB

new patch. not sure if it'll green or not, i may have missed something when switching over to the new named params. they are:

interface DiscoveryInterface {

  const EXCEPTION_ON_INVALID_ID = 1;
  const NULL_ON_INVALID_ID = 2;

}

seem workable? 'ignore' seems rather the same as null, so i didn't think it applicable here.

note that this:

PluginManagerBase::getDefinition() now has its own try/catch block in it, wherein it waits for one of these PluginNotFoundExceptions, catches it, then throws a new one but includes the current class name in it. this is the simplest thing i could come up with to provide more context than simply the plugin id..however, still less than optimal.

is still as it was. even worse, actually, it now reflects on the exception to generate a new one of the same type. i added a @todo for it, it desperately needs improving, but i need someone with more insight into what info we currently/could reliably provide to discovery plugins to figure out a better approach, i think.

clemens.tolboom’s picture

Status: Needs review » Needs work

The last submitted patch, discovery-exceptions-1846070-12.patch, failed testing.

clemens.tolboom’s picture

clemens.tolboom’s picture

Status: Needs review » Needs work
clemens.tolboom’s picture

Issue summary: View changes

fix doubling of API changes section

xano’s picture

Status: Needs work » Needs review
StatusFileSize
new26.04 KB

What about something like this? Instead of using constants to alter methods' behaviors, we simply add has*() methods that allow us to fail gracefully by checking for something before we request it.

The code is untested and probably not complete, but you'll get the idea.

damiankloip’s picture

Then should has() be used in calling code too? We could also think about getting more info in that exception message.

yched’s picture

tim.plunkett’s picture

Parts of this are unknowingly duplicated by #2178795: Allow DiscoveryInterface::getDefinition() to throw an exception for an invalid plugin (the hasDefinition).
The other part are explicitly duplicated in #2189985: Rename UnknownPluginException to PluginNotFoundException, which is my fault...
I could close that if you'd rather continue here, but that one was quick and RTBC.

xano’s picture

Then should has() be used in calling code too? We could also think about getting more info in that exception message.

If you really only want to check whether a plugin exists, or if you want to be able to degrade gracefully (using a fallback plugin, for instance), than calling has() is the way to go. The exception should ideally not be thrown on live sites as it should only occur when a developer makes a typo in a plugin ID, for instance.

I'll take a look at #2178795: Allow DiscoveryInterface::getDefinition() to throw an exception for an invalid plugin. This patch doesn't only touch DiscoveryInterface, though, but also deals with derivers and derivatives, for instance.

xano’s picture

Also #1875996: Reconsider naming conventions for derivative classes will pave the way for more self-explanatory exception names.

neclimdul’s picture

Just noticed, probably more appropriate to make this an InvalidArgumentException. Just implement the Plugin "ExceptionInterface" instead.

wim leers’s picture

Issue tags: +Needs reroll
nitesh sethia’s picture

Assigned: Unassigned » nitesh sethia

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

lokeshwaran97’s picture

Status: Needs review » Needs work
StatusFileSize
new25.85 KB

patch doesn't work

vijaycs85’s picture

Assigned: nitesh sethia » Unassigned

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

MerryHamster’s picture

Version: 8.5.x-dev » 8.6.x-dev

as I understand it's not actual for 8.6, the structure has many changes.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

mpp’s picture

We can (should) improve the wording to clarify the problem.

But the problem remains that you'll run into this error when adding a plugin to an existing module:

Plugin (my_plugin) instance class "Drupal\my_module\Plugin\ConfigFilter\MyPlugin" does not exist. In DefaultFactory.php line 97.

The discovery will find the plugin id but the class is not yet loaded (a new plugin requires a cache-clear).

There are a large number of issues in the queue describing this error message, some have a faulty namespace but in a number of cases it's due to cached plugins.

There is an issue that should solve this for plugin derivers but I couldn't find one for plugins.

adityaj’s picture

Drupal plugin system has issues, it in some cases crashes the whole site when the plugin is missing. We should have some drush command to recover from site crash. Or plugin system should auto recover it self and prevent site crash.

renguer0’s picture

Same problem here. I'm uploaded my site from localhost, database connects but I see this error message when I try to get into drupal admin page.

Looking for a solution. Thanks for your time.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

jungle’s picture

  1. +++ b/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php
    @@ -76,11 +78,27 @@ protected function getAnnotationReader() {
    +    if (isset($plugin_definitions[$plugin_id])) {
    +      return $plugin_definitions[$plugin_id];
    ...
    +    else {
    

    else here is unnecessary, as in if, there is a return

  2. +++ b/core/lib/Drupal/Component/Plugin/Discovery/DerivativeDiscoveryDecorator.php
    @@ -185,18 +210,35 @@ protected function getDerivativeFetcher($base_plugin_id, array $base_definition)
    +    else {
    +      throw new DerivativeFetcherNotFoundException('Plugin @plugin_id does not specify a derivative fetcher.', array(
    +        '@plugin_id' => $base_definition['id'],
    +      ));
    

    Similar to the above i guess

  3. +++ b/core/lib/Drupal/Component/Plugin/Discovery/StaticDiscovery.php
    @@ -21,10 +23,24 @@ class StaticDiscovery implements DiscoveryInterface {
    +    else {
    +      throw new PluginNotFoundException(String::format('The plugin %plugin_id could not be found.', array(
    +        '%plugin_id' => $plugin_id,
    

    And here

  4. And more similar to the above ...

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

narendra.rajwar27’s picture

Assigned: Unassigned » narendra.rajwar27
narendra.rajwar27’s picture

Status: Needs work » Needs review
StatusFileSize
new16.66 KB

applied the patch after making changes.

narendra.rajwar27’s picture

StatusFileSize
new16.78 KB
narendra.rajwar27’s picture

StatusFileSize
new14.93 KB
narendra.rajwar27’s picture

Assigned: narendra.rajwar27 » Unassigned
sivaji_ganesh_jojodae’s picture

Issue tags: -Needs reroll
quietone’s picture

@narendra.rajwar27, Thank you for looking into to this issue and working to get a viable patch. The rerolls in comment #43, #44 and #45 do not have interdiffs (or a diff) so it isn't clear what changes have been made or why. You are more likely to get a review if the interdiff is provided as well as a comment describing what you have changed and why. This makes it much easier for the reviewer to understand the changes.

This issue began in 2012 and much has changed. The proposed resolution in the IS suggests adding a new PluginNotFoundException. That exception already exists, \Drupal\Component\Plugin\Exception\PluginNotFoundException, so let's have an issue summary update.

quietone’s picture

Issue tags: -Bug Smash Initiative

Checking my work and this isn't a bug and I didn't look at this as part of Bug Smash Initiative work, so removing tag.

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

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

nwom’s picture

StatusFileSize
new14.79 KB

The patch no longer applies against 9.2.x. Here is a re-roll without any changes.

nwom’s picture

Status: Needs review » Needs work

Even after re-rolling, it appears there is another issue. I get a WSOD with the following message in watchdog:

Error: Class 'Drupal\Component\Utility\SafeMarkup' not found in Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator->getDefinition() (line 83 of /core/lib/Drupal/Component/Plugin/Discovery/DerivativeDiscoveryDecorator.php)

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.

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.

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.

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.