Magento 2 Command-Line – Sending Email Using Block Templates Error: Missing Required Argument $debugHintsPath

command lineemail-templatesmagento2order-email

When attempting to send emails in Magento 2 from command-line, I encountered the exception below. While using the very same class to send emails from an frontend or backend controller was working perfectly. The issue was stricly happening using the command line interface.

Exception:

main.CRITICAL: exception 'BadMethodCallException' with message 'Missing required argument $debugHintsPath of Magento\Developer\Model\TemplateEngine\Plugin\DebugHints.' in /…/…/magento/vendor/magento/framework/ObjectManager/Factory/Dynamic/Developer.php:45

The issue was also only happening when trying to call a block via layout from inside the template. As soon as the block call was removed, the exception stopped showing.

Template File:

app/code/NameSpace/Module/view/frontend/email/email_notification.html

{{template config_path="design/email/header_template"}}

...

<!-- THIS LINE CAUSED THE EXCEPTION TO SHOW UP -->
{{layout handle="sales_email_order_items" order=$order area="frontend"}}

...

{{template config_path="design/email/footer_template"}}

The email was still sent with the subject line intact but the entire content was not rendered and only the error below was showing in the content section once the email was received.

Error printed inside emails:

Error filtering template: Missing required argument $debugHintsPath of Magento\Developer\Model\TemplateEngine\Plugin\DebugHints.

Best Answer

I finally found the solution to this problem in the Magento Community Forums, which was provided by @dunagan5887. I decided to share it here on magento.stackexchange.com as many may benefit from a well refered solution to this exception.

There is a link to the original Community Forum post: Email template with block

It seems that this solution, as quoted by @dunagan5887; dictates that the di.xml directive set in vendor/magento/module-developer/etc/adminhtml/di.xml is loaded.

The Solution Consists of this Simple Line of Code:

$this->_objectManager->configure($this->_configLoader->load('adminhtml'));


Please find a working version command line class below:

app/code/NameSpace/Module/Console/Command.php

<?php
namespace NameSpace\Module\Console\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Magento\Framework\Exception\LocalizedException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class CustomCommandClass extends Command
{
    public function __construct(
        \Magento\Framework\App\State $state,
        \Magento\Framework\ObjectManagerInterface $objectManager,
        \Magento\Framework\ObjectManager\ConfigLoaderInterface $configLoader
    ) {
        $state->setAreaCode('frontend'); //SET CURRENT AREA
        $objectManager->configure($configLoader->load('frontend')); //SOLUTION
        parent::__construct();
    }

    ...

}

Simply change the area from frontend to admin or global as required by your application.


[UPDATE]

Area adminhtml causing static content deploy errors

It seems that for some reasons setting the area to adminhtml is causing some errors while deploying static contents.

We were seeing errors like the following:

Fatal error: Uncaught Exception: Warning: Error while sending QUERY packet. PID=22912 in ../magento/vendor/magento/zendframework1/library/Zend/Db/Statement/Pdo.php on line 228 in ../magento/vendor/magento/framework/App/ErrorHandler.php:61

I initially thought that this error would be caused by a low max_allowed_packet setting for MYSQL but as the limit was already high enough and raising it wasn't resolving the issue, I decided to dig further. After going through an elimination process I finally found out that this was the main difference between two modules using similar command functions, from which one of the modules was causing this issue as soon as enabled.

Although I haven't digged to find the source of this issue or conflict, I thought it would be a good idea to share my findings here as others may find it useful.


[UPDATE - 2]

The right method:

After upgrading Magento to 2.2.X we realized that this is the right method for setting the area:

app/code/NameSpace/Module/Console/Command.php

<?php
namespace NameSpace\Module\Console\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Magento\Framework\Exception\LocalizedException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class CustomCommandClass extends Command
{
    public function __construct(
        \Magento\Framework\App\State $state,
    ) {
        $this->_appState = $appState;
        parent::__construct();
    }

    ...

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $this->_appState->setAreaCode(\Magento\Framework\App\Area::AREA_GLOBAL); //SET CURRENT AREA

        ...

    }

    ...

}

Note that we do not make use of the Object Manager and that the area has to be set within the function requiring it and NOT in the constructor. This is the official way of setting the area and it should work flawlessly with all Magento 2 versions.


A list of the available areas is available in the following class:

Magento\Framework\App\Area

class Area implements \Magento\Framework\App\AreaInterface
{
    const AREA_GLOBAL = 'global';
    const AREA_FRONTEND = 'frontend';
    const AREA_ADMIN    = 'admin';
    const AREA_ADMINHTML = 'adminhtml';
    const AREA_DOC = 'doc';
    const AREA_CRONTAB = 'crontab';
    const AREA_WEBAPI_REST = 'webapi_rest';
    const AREA_WEBAPI_SOAP = 'webapi_soap';

    ...