TypeError: Argument 1 passed to Drupal\commerce\Country::__construct() must be of the type string, null given

Created on 29 September 2020, about 4 years ago
Updated 17 May 2024, 7 months ago

Problem/Motivation

When I trying to import from config, I've stucked with error:

[error]  TypeError: Argument 1 passed to Drupal\commerce\Country::__construct() must be of the type string, null given, called in /var/www/html/web/modules/contrib/commerce/src/Resolver/DefaultCountryResolver.php on line 35 in Drupal\commerce\Country->__construct() (line 23 of /var/www/html/web/modules/contrib/commerce/src/Country.php) #0 /var/www/html/web/modules/contrib/commerce/src/Resolver/DefaultCountryResolver.php(35): Drupal\commerce\Country->__construct()
#1 /var/www/html/web/modules/contrib/commerce/src/Resolver/ChainCountryResolver.php(46): Drupal\commerce\Resolver\DefaultCountryResolver->resolve()
#2 /var/www/html/web/modules/contrib/commerce/src/CurrentCountry.php(62): Drupal\commerce\Resolver\ChainCountryResolver->resolve()
#3 /var/www/html/web/modules/contrib/commerce/src/Resolver/DefaultLocaleResolver.php(54): Drupal\commerce\CurrentCountry->getCountry()
#4 /var/www/html/web/modules/contrib/commerce/src/Resolver/ChainLocaleResolver.php(46): Drupal\commerce\Resolver\DefaultLocaleResolver->resolve()
#5 /var/www/html/web/modules/contrib/commerce/src/CurrentLocale.php(62): Drupal\commerce\Resolver\ChainLocaleResolver->resolve()
#6 /var/www/html/web/modules/contrib/commerce/modules/price/src/CurrencyFormatter.php(29): Drupal\commerce\CurrentLocale->getLocale()
#7 [internal function]: Drupal\commerce_price\CurrencyFormatter->__construct()
#8 /var/www/html/vendor/symfony/dependency-injection/ContainerBuilder.php(1176): ReflectionClass->newInstanceArgs()
#9 /var/www/html/vendor/symfony/dependency-injection/ContainerBuilder.php(634): Symfony\Component\DependencyInjection\ContainerBuilder->createService()
#10 /var/www/html/vendor/symfony/dependency-injection/ContainerBuilder.php(588): Symfony\Component\DependencyInjection\ContainerBuilder->doGet()
#11 /var/www/html/web/modules/contrib/commerce/modules/price/src/Plugin/Field/FieldFormatter/PriceDefaultFormatter.php(72): Symfony\Component\DependencyInjection\ContainerBuilder->get()
#12 /var/www/html/web/core/lib/Drupal/Core/Field/FormatterPluginManager.php(64): Drupal\commerce_price\Plugin\Field\FieldFormatter\PriceDefaultFormatter::create()
#13 /var/www/html/web/core/lib/Drupal/Core/Plugin/DefaultLazyPluginCollection.php(81): Drupal\Core\Field\FormatterPluginManager->createInstance()
#14 /var/www/html/web/core/lib/Drupal/Component/Plugin/LazyPluginCollection.php(80): Drupal\Core\Plugin\DefaultLazyPluginCollection->initializePlugin()
#15 /var/www/html/web/core/lib/Drupal/Component/Plugin/LazyPluginCollection.php(148): Drupal\Component\Plugin\LazyPluginCollection->get()
#16 /var/www/html/web/core/lib/Drupal/Core/Plugin/DefaultLazyPluginCollection.php(114): Drupal\Component\Plugin\LazyPluginCollection->getIterator()
#17 /var/www/html/web/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php(292): Drupal\Core\Plugin\DefaultLazyPluginCollection->getConfiguration()
#18 /var/www/html/web/core/lib/Drupal/Core/Entity/EntityDisplayBase.php(272): Drupal\Core\Config\Entity\ConfigEntityBase->preSave()
#19 /var/www/html/web/core/modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php(126): Drupal\Core\Entity\EntityDisplayBase->preSave()
#20 /var/www/html/web/core/lib/Drupal/Core/Entity/EntityStorageBase.php(499): Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay->preSave()
#21 /var/www/html/web/core/lib/Drupal/Core/Entity/EntityStorageBase.php(454): Drupal\Core\Entity\EntityStorageBase->doPreSave()
#22 /var/www/html/web/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php(263): Drupal\Core\Entity\EntityStorageBase->save()
#23 /var/www/html/web/core/lib/Drupal/Core/Entity/EntityBase.php(395): Drupal\Core\Config\Entity\ConfigEntityStorage->save()
#24 /var/www/html/web/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php(616): Drupal\Core\Entity\EntityBase->save()
#25 /var/www/html/web/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php(364): Drupal\Core\Config\Entity\ConfigEntityBase->save()
#26 /var/www/html/web/core/lib/Drupal/Core/Config/ConfigImporter.php(997): Drupal\Core\Config\Entity\ConfigEntityStorage->importCreate()
#27 /var/www/html/web/core/lib/Drupal/Core/Config/ConfigImporter.php(783): Drupal\Core\Config\ConfigImporter->importInvokeOwner()
#28 /var/www/html/web/core/lib/Drupal/Core/Config/ConfigImporter.php(610): Drupal\Core\Config\ConfigImporter->processConfiguration()
#29 /var/www/html/web/core/lib/Drupal/Core/Config/ConfigImporter.php(514): Drupal\Core\Config\ConfigImporter->processConfigurations()
#30 /var/www/html/web/core/lib/Drupal/Core/Config/Importer/ConfigImporterBatch.php(31): Drupal\Core\Config\ConfigImporter->doSyncStep()
#31 /var/www/html/web/core/includes/batch.inc(295): Drupal\Core\Config\Importer\ConfigImporterBatch::process()
#32 /var/www/html/web/core/includes/form.inc(947): _batch_process()
#33 /var/www/html/web/core/includes/install.core.inc(652): batch_process()
#34 /var/www/html/web/core/includes/install.core.inc(570): install_run_task()
#35 /var/www/html/web/core/includes/install.core.inc(118): install_run_tasks()
#36 /var/www/html/vendor/drush/drush/includes/drush.inc(213): install_drupal()
#37 /var/www/html/vendor/drush/drush/includes/drush.inc(197): drush_call_user_func_array()
#38 /var/www/html/vendor/drush/drush/src/Commands/core/SiteInstallCommands.php(149): drush_op()
#39 [internal function]: Drush\Commands\core\SiteInstallCommands->install()
#40 /var/www/html/vendor/consolidation/annotated-command/src/CommandProcessor.php(257): call_user_func_array()
#41 /var/www/html/vendor/consolidation/annotated-command/src/CommandProcessor.php(212): Consolidation\AnnotatedCommand\CommandProcessor->runCommandCallback()
#42 /var/www/html/vendor/consolidation/annotated-command/src/CommandProcessor.php(176): Consolidation\AnnotatedCommand\CommandProcessor->validateRunAndAlter()
#43 /var/www/html/vendor/consolidation/annotated-command/src/AnnotatedCommand.php(302): Consolidation\AnnotatedCommand\CommandProcessor->process()
#44 /var/www/html/vendor/symfony/console/Command/Command.php(255): Consolidation\AnnotatedCommand\AnnotatedCommand->execute()
#45 /var/www/html/vendor/symfony/console/Application.php(1005): Symfony\Component\Console\Command\Command->run()
#46 /var/www/html/vendor/symfony/console/Application.php(255): Symfony\Component\Console\Application->doRunCommand()
#47 /var/www/html/vendor/symfony/console/Application.php(148): Symfony\Component\Console\Application->doRun()
#48 /var/www/html/vendor/drush/drush/src/Runtime/Runtime.php(118): Symfony\Component\Console\Application->run()
#49 /var/www/html/vendor/drush/drush/src/Runtime/Runtime.php(49): Drush\Runtime\Runtime->doRun()
#50 /var/www/html/vendor/drush/drush/drush.php(72): Drush\Runtime\Runtime->run()
#51 /var/www/html/vendor/drush/drush/drush(4): require('/var/www/html/v...')
#52 {main}. 

After researching I found the place that called this error:
commerce/src/Resolver/DefaultCountryResolver.php:35

  public function resolve() {
    $country_code = $this->configFactory->get('system.date')->get('country.default');
    return new Country($country_code);
  }

We trying to get the $country_code value from settings, but even when this data is provided in system.date.yml in your configs, we still get NULL here.
And as result we got the error that was present above.

I'm not sure why we got NULL here, but looks like we have to add an additional condition here like in others resolvers.

Steps to reproduce

I can't provide steps for reproduce, because not sure how to reproduce that on other environment.

Proposed resolution

Provide a patch that should fix this issue

🐛 Bug report
Status

Fixed

Version

2.0

Component

Commerce

Created by

🇺🇦Ukraine aleevas

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

Merge Requests

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • 🇨🇭Switzerland berdir Switzerland

    This became more relevant in 10.3, which removes the country setting and the implicit empty string value being set there, resulting in this fatal error with basically all tests on 10.3 right now. See 📌 Remove country setting from the installer Fixed . This might still change, but it doesn't hurt to make this change. It's quite possible that the setting will be completely removed from core in the future.

  • 🇨🇭Switzerland berdir Switzerland

    The relevant core issue is actually 📌 Add validation constraints to system.date Fixed

  • 🇺🇸United States nicxvan

    Bumping priority because this will become a larger issue for new installs once 10.3 comes out I think.

  • 🇺🇸United States nicxvan

    nicxvan changed the visibility of the branch 3173772-typeerror-argument-1 to hidden.

  • 🇺🇸United States nicxvan

    nicxvan changed the visibility of the branch 3173772-typeerror-argument-1 to active.

  • 🇺🇸United States nicxvan

    nicxvan changed the visibility of the branch 8.x-2.x to hidden.

  • Merge request !254Apply patch → (Merged) created by nicxvan
  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.x + Environment: PHP 8.0 & MySQL 5.7
    last update 8 months ago
    793 pass
  • Status changed to Needs review 8 months ago
  • 🇺🇸United States nicxvan

    I converted this to an MR, I think this might need some work though, shouldn't resolve return a Country no matter what?

    I imagine we'd follow what Price does in .install and set a specific country as default if one is not set on the site.

    I'm moving to needs review for that question.

  • 🇺🇸United States nicxvan

    Also should we just remove the three references entirely?

  • Pipeline finished with Success
    8 months ago
    Total: 592s
    #162968
  • 🇨🇭Switzerland berdir Switzerland

    At least the local resolver, which uses the country resolver checks if there really is a return value.

    Because it was optional before as well, the only change is the move from empty string which was bogus but not broken, to null.

  • 🇮🇱Israel jsacksick

    So if there is a possibly to have a empty string, shouldn't we simply check if the country code isn't empty? And return NULL otherwise? Instead of having an instantiated country with an "" code?

  • 🇨🇭Switzerland berdir Switzerland

    That's what the code kinda does, I think the is_string() call is not necessary. The difference is just that an empty string kinda worked until now, while a null value doesn't work at all.

  • 🇧🇪Belgium wim leers Ghent 🇧🇪🇪🇺

    Found this thanks to @Berdir's comment at #3437325-42: Add validation constraints to system.date .

    Interesting:

    [error]  TypeError: Argument 1 passed to Drupal\commerce\Country::__construct() must be of the type string, null given, called in /var/www/html/web/modules/contrib/commerce/src/Resolver/DefaultCountryResolver.php on line 35 in Drupal\commerce\Country->__construct() (line 23 of /var/www/html/web/modules/contrib/commerce/src/Country.php) #0 
    

    👆 None of that code is in Drupal core: neither the Country class nor the DefaultCountryResolver class.

    It seems that the problem here is that this module has built infrastructure that assumes a certain data shape ("strings only"), but it does not control the shape: Drupal core does.

    Because of that (fine!) choice, this module's infrastructure should adhere to https://en.wikipedia.org/wiki/Robustness_principle: — in this case it probably should detect whether the data is anything else than a 2-character string, and if so, fall back to the empty string (which this infra has assumed to be the fallback value so far).

  • 🇮🇱Israel jsacksick

    I'd be in favor of returning NULL instead of instantianting a country with an empty string...
    The CountryResolverInterface has the following documentation.

     * Resolves the country.
       *
       * @return \Drupal\commerce\Country|null
       *   The country object, if resolved. Otherwise NULL, indicating that the next
       *   resolver in the chain should be called.
       */

    In case of an empty string, a country would be considered as resolved, thus the next resolver in the chain isn't going to be invoked, which sounds like a bug to me.
    So IMO we should simply check if it's empty, or check if the config value is a 2 characters string.

  • 🇨🇭Switzerland berdir Switzerland

    @jsacksick? Yes, that's exactly what the MR does? It additionally also checks for it to be a string to be extra careful, just in case that config is something else, but an empty string will skip the check and return NULL

  • 🇮🇱Israel jsacksick

    @Berdir: No, I don't want a country to be resolved when the string is empty "".

    if ($country_code && is_string($country_code))
    An empty string will pass the condition?

  • Status changed to Fixed 8 months ago
  • 🇮🇱Israel jsacksick

    Merge the MR, thanks!

  • 🇺🇸United States nicxvan

    Thanks! I also just want to make it clear, this probably wants to make it into a recommended release before Drupal 10.3 drops since that introduces the upstream change.

  • 🇮🇱Israel jsacksick

    It seems we have quite some time ahead of us (Drupal 10.3 will be released on the week of June 17, 2024).
    But in any case, a new Commerce release is already a bit overdue, I know several contrib patches are depending on code that is already in dev but not yet part of a release.

  • Automatically closed - issue fixed for 2 weeks with no activity.

Production build 0.71.5 2024