Unhandled exceptions in ComponentPluginManager

Created on 5 March 2025, about 2 months ago

Overview

In the ComponentPluginManager::setCachedDefinitions($definitions) there are some calls to ComponentPluginManager::createInstance that can result in an unhandled exception if the component machine name does not exit for any reason (custom code,...), because the "machine_name" comes from the $definitions argument, so could be wrong.

Proposed resolution

Handle properly the Exceptions with the ComponentNotFoundException

🐛 Bug report
Status

Active

Version

0.0

Component

Code

Created by

🇪🇸Spain isholgueras

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @isholgueras
  • Pipeline finished with Failed
    about 2 months ago
    Total: 6695s
    #440497
  • Pipeline finished with Failed
    about 2 months ago
    Total: 1503s
    #440667
  • Pipeline finished with Failed
    about 2 months ago
    Total: 1815s
    #440699
  • 🇧🇪Belgium wim leers Ghent 🇧🇪🇪🇺

    Thanks for reporting this! At first I thought this was indeed an oversight, and then I realized that actually … I don't yet understand how to hit this case? 🤔😅

    See https://git.drupalcode.org/project/experience_builder/-/merge_requests/7... for details. 😊

  • 🇧🇪Belgium wim leers Ghent 🇧🇪🇪🇺
  • Pipeline finished with Failed
    about 2 months ago
    Total: 4168s
    #440732
  • 🇪🇸Spain isholgueras

    how did you end up in this situation? 🤔 🙏

    I was unable to reproduce again. It happened after a couple of fresh install ddev xb-setup --force and visiting the first time the xb/node/1. I've got an error and I saw the ComponentNotFoundException in the dblog, but only once.

    Since then, I'm unable to reproduce, even with some specific KernelTest with an unstable state. Maybe My installation was corrupted and the component listed wasn't the same as the components in the cache (from... the previous installation 🤔?) Don't know.

    Also, I saw PhpStorm reporting that these 2 calls have an Unhandled exception:

    So these were the 2 "ways" I've ended with the exception.

    Maybe we can ignore this try/catch until we can find a reliable and stable test.

  • 🇧🇪Belgium wim leers Ghent 🇧🇪🇪🇺

    Also, I saw PhpStorm reporting that these 2 calls have an Unhandled exception:

    That's because PHPStorm only is able to look at the code here, it does not understand that a Component config entity cannot exist without the SDC plugin actually existing, meaning a ComponentNotFoundException is impossible when using/operating a site as a site builder!

    Since then, I'm unable to reproduce

    I think can see a way to reproduce: install module foo containing SDC bar. XB will create a Component config entity for foo:bar if it meets XB's requirements. Then delete the foo:bar SDC from the filesystem, as if you're actively developing it.

    That is when you can run into it: as a SDC developer.

    🙏 Could you confirm that?

    Related: 📌 SDC *.component.yml metadata is cached aggressively, gets in the way of component development Active .

  • 🇪🇸Spain isholgueras

    I think can see a way to reproduce: install module foo containing SDC bar. XB will create a Component config entity for foo:bar if it meets XB's requirements. Then delete the foo:bar SDC from the filesystem, as if you're actively developing it.

    Makes sense. I'll try to reproduce it and if so, I'll make a test.

    Thanks!

  • 🇧🇪Belgium wim leers Ghent 🇧🇪🇪🇺

    Thank you 😄, for taking this on! 🤩🙏

  • 🇪🇸Spain isholgueras

    Ok, after a deep search, I think it's currently well handle with 2 asserts.

    I've created a custom module (nacho) with one simple component:

    // nacho/components/nacho-component.twig
    It's me, {{ text }}!
    
    // nacho/components/nacho-component.component.yml
    $schema: https://git.drupalcode.org/project/sdc/-/raw/1.x/src/metadata.schema.json
    name: Nacho component
    props:
      type: object
      properties:
        text:
          type: string
          title: Name
          description: The name to show.
          examples: ['Nacho']
    

    After clearing cache, the component appears in the admin UI, so I add this component to the page, add a name and save it.

    The /node/1 page looks good.

    Then I uninstall nacho module and I get 2 errors in 2 different pages, the /node/1 and /xb/node/1/editor. Don't know if expected, but 2 errors because the source of the component no longer exist.

    The error in /node/1 is triggered in `ComponentTreeHidrated.php:48`.

          $component_id = $tree->getComponentId($uuid);
          $component = Component::load($component_id);
          assert($component instanceof Component);
    

    $component is null so the assert throws an error.

    The second error, happens in /xb/node/1/editor, and is triggered by ApiLayoutController.php:192

          $source = $tree->getComponentSource($component_instance_uuid);
          \assert($source instanceof ComponentSourceInterface);
          if ($source->requiresExplicitInput()) {
            $model[$component_instance_uuid] = $source->inputToClientModel($source->getExplicitInput($component_instance_uuid, $item));
          }
    

    $source is null so the assert throws an error.

    Even though, I still couldn't reproduce the initial issue. The createInstance method is well covered by the asserts. I've tested Kernel and Unit tests and I'm still unable to mock a scenario where this Exception happen in the experience_builder module.

    So at this point I think is up to you (maintainers). Ignoring it and including it for an edge case, and I believe that this edge case ever happen will be easily identified and solved.

  • 🇧🇪Belgium wim leers Ghent 🇧🇪🇪🇺

    Thanks for your thorough test! 🙏

    I have a few thoughts that I think indicate a different perspective is still needed, AFAICT you approached it from a "strong PHP/Drupal module developer" POV? 🤓 (Yes that's also a compliment! 😄) Here, we need to approach it from the "person who knows SDCs and nothing else" POV.

    the component appears in the admin UI, so I add this component to the page, add a name and save it.
    […]
    Then I uninstall nacho module

    It is not possible to uninstall this module when there's a config entity depending on it. Did you force the uninstallation using drush, perhaps?

    I believe that this edge case ever happen will be easily identified and solved.

    Imagine a developer who knows only SDC — I doubt they would be able to easily identify that assert($component instanceof Component); ties back to their deleted/renamed SDC? 😅

    (Also: assert() only is executed on dev environments, and probably only when writing PHP code, not when developing SDCs.)

  • 🇧🇪Belgium wim leers Ghent 🇧🇪🇪🇺
  • 🇪🇸Spain isholgueras

    It is not possible to uninstall this module when there's a config entity depending on it. Did you force the uninstallation using drush, perhaps?

    Yes, exactly. I've used drush. You know, when your developing a module you could go back and forth with the installations and you could uninstall something by mistake, or an incomplete or unstable config,...

    assert() only is executed on dev environments, and probably only when writing PHP code, not when developing SDCs.

    Yes, I've commented the asserts (to mimic a non dev environment) and I get the typical errors of "Unable to get ->requiresExplicitInput() from null" and so forth. But couldn't reach that try.

    That's right. I'll try to investigate it as a SDC developer POV :). Let's see how far I can get.

    Thanks for the guidance!

  • 🇧🇪Belgium wim leers Ghent 🇧🇪🇪🇺
  • 🇧🇪Belgium wim leers Ghent 🇧🇪🇪🇺
  • Pipeline finished with Failed
    23 days ago
    Total: 859s
    #463267
  • Pipeline finished with Failed
    23 days ago
    Total: 1665s
    #463283
Production build 0.71.5 2024