{"record":{"id":"634a8f0a1ebb9b68","repo":"phalcon/cphalcon","slug":"a-dependency-injection-container-is-required-to-ac-634a8f","errorCode":null,"errorMessage":"A dependency injection container is required to access the 'escaper' service","messagePattern":"A dependency injection container is required to access the 'escaper' service","errorType":"exception","errorClass":"Phalcon\\Tag\\Exception","httpStatus":null,"severity":"error","filePath":"phalcon/Tag.zep","lineNumber":418,"sourceCode":"        }\n\n        return self::getEscaperService();\n    }\n\n    /**\n     * Returns an Escaper service from the default DI\n     */\n    public static function getEscaperService() -> <EscaperInterface>\n    {\n        var escaper, container;\n\n        let escaper = self::escaperService;\n\n        if typeof escaper != \"object\" {\n            let container = self::getDI();\n\n            if container === null {\n                throw new Exception(\n                    \"A dependency injection container is required to access the 'escaper' service\"\n                );\n            }\n\n            let escaper = <EscaperInterface> container->getShared(\"escaper\"),\n                self::escaperService = escaper;\n        }\n\n        return escaper;\n    }\n\n    /**\n     * Gets the current document title. The title will be automatically escaped.\n     */\n    public static function getTitle(\n        bool prepend = true,\n        bool append = true\n    ) -> string {","sourceCodeStart":400,"sourceCodeEnd":436,"githubUrl":"https://github.com/phalcon/cphalcon/blob/b7419de9cd0a8a3f48441ead84c9f8415d463e25/phalcon/Tag.zep#L400-L436","documentation":"Phalcon\\Tag's HTML helpers escape output through the 'escaper' shared service resolved from the default DI container (Tag::getDI() → Di::getDefault(); the resolved EscaperInterface is then cached statically in Tag::$escaperService). If no default container exists, getEscaperService() throws Phalcon\\Tag\\Exception (\"A dependency injection container is required to access the 'escaper' service\"). FactoryDefault registers 'escaper' (Phalcon\\Html\\Escaper) automatically — the error means Tag ran outside a booted application.","triggerScenarios":"Calling Tag::textField([...]) / Tag::renderAttributes() / any tag helper in a PHPUnit test or CLI script with no container; after Di::reset() in test teardown; note the static escaperService cache makes later calls succeed, producing order-dependent failures.","commonSituations":"Unit tests rendering tags in isolation; queue workers or CLI tools that include view partials; forgetting to set the DI container (Di::setDefault or Tag::setDI) in a custom bootstrap.","solutions":["Set a container before rendering: Tag::setDI($di) with $di->setShared('escaper', Escaper::class), or Di::setDefault(new FactoryDefault())","In tests, prime the container once in setUp() and reset it in tearDown() to avoid order dependence","Restructure code so tag helpers are only used inside a fully bootstrapped request"],"exampleFix":"// before (CLI script / unit test)\necho \\Phalcon\\Tag::textField(['name' => 'email']); // throws\n\n// after\n$di = new \\Phalcon\\Di\\Di();\n$di->setShared('escaper', \\Phalcon\\Html\\Escaper::class);\n\\Phalcon\\Di\\Di::setDefault($di);\n\\Phalcon\\Tag::setDI($di);\n\necho \\Phalcon\\Tag::textField(['name' => 'email']);","handlingStrategy":"validation","validationCode":"use Phalcon\\Di\\Di;\nuse Phalcon\\Html\\Escaper;\nuse Phalcon\\Tag;\n\nif (null === Di::getDefault()) {\n    $di = new Di();\n    $di->setShared('escaper', Escaper::class);\n    Di::setDefault($di);\n    Tag::setDI($di);\n}\n\necho Tag::textField(['name' => 'email']);","typeGuard":"function hasTagEscaperContainer(): bool\n{\n    return null !== \\Phalcon\\Di\\Di::getDefault()\n        || null !== \\Phalcon\\Tag::getDI(); // cached static check\n}","tryCatchPattern":"try {\n    echo \\Phalcon\\Tag::textField(['name' => 'email']);\n} catch (\\Phalcon\\Tag\\Exception $e) {\n    if (str_contains($e->getMessage(), \"'escaper' service\")) {\n        // container missing: escape manually and continue\n        echo '<input type=\"text\" name=\"' . htmlspecialchars('email', ENT_QUOTES) . '\">';\n    } else {\n        throw $e;\n    }\n}","preventionTips":["Bootstrap the container (Di::setDefault or Tag::setDI) before any tag helper runs — including tests and CLI","In test base classes, set up the container in setUp() and Di::reset() in tearDown() to avoid static-cache order effects","Register 'escaper' explicitly when using a bare Di instead of FactoryDefault"],"tags":["phalcon","tag","dependency-injection","bootstrap"],"backgroundTag":"missing-di-container","analyzedSha":"b7419de9cd0a8a3f48441ead84c9f8415d463e25","analyzedAt":"2026-08-21T06:21:18.811Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}