{"record":{"id":"8dae9126b88f1bef","repo":"octobercms/october","slug":"the-markup-filter-function-for-this-name-is","errorCode":null,"errorMessage":"The markup filter/function for '{$this->name}' is not callable.","messagePattern":"The markup filter/function for '(.+?)' is not callable\\.","errorType":"exception","errorClass":"ApplicationException","httpStatus":500,"severity":"error","filePath":"modules/system/classes/MarkupExtensionItem.php","lineNumber":170,"sourceCode":"\n    /**\n     * getTwigCallback returns a callback function suitable for Twig\n     */\n    public function getTwigCallback()\n    {\n        // Handle a wildcard function\n        if (strpos($this->name, '*') !== false && $this->isWildCallable()) {\n            return function ($name) {\n                $arguments = array_slice(func_get_args(), 1);\n                $method = $this->getWildCallback(Str::camel($name));\n                return $method(...$arguments);\n            };\n        }\n\n        // Cannot call item method\n        $callable = $this->callback;\n        if (!is_callable($callable)) {\n            throw new ApplicationException(\"The markup filter/function for '{$this->name}' is not callable.\");\n        }\n\n        // Wrap in a closure to prevent Twig from reflecting facades\n        // when applying its named closure support\n        return function(...$args) use ($callable) {\n            return $callable(...$args);\n        };\n    }\n\n    /**\n     * getTwigOptions returns options passed to the Twig definition\n     */\n    public function getTwigOptions(): array\n    {\n        return ($this->escapeOutput ? [] : ['is_safe' => ['html']]) + $this->options;\n    }\n\n    /**","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/octobercms/october/blob/b608633a7e8922487d91a8161499020121c3b3bf/modules/system/classes/MarkupExtensionItem.php#L152-L188","documentation":"MarkupExtensionItem wraps a Twig filter or function registered through a plugin's registerMarkupTags() (or CMS markup extensions). getTwigCallback() is called when the item is bound into Twig; for non-wildcard names it checks the stored callback with is_callable() and throws ApplicationException naming the filter/function when the callback cannot actually be invoked. Wildcard registrations (e.g. 'str_*' routing to Str::*) bypass this check and resolve dynamically.","triggerScenarios":"registerMarkupTags() returning ['filters' => ['excerpt' => 'acme_excerpt']] where that function does not exist; ['functions' => ['tweet' => ['Acme\\Blog\\Classes\\Twitter', 'send']]] where send is not a public static method or the class name has a typo; callbacks written in 'Class@method' string form, which PHP does not treat as callable; referencing a class that is not autoloadable at registration time.","commonSituations":"First render of a page/template after adding a new markup tag; renaming a helper class without updating registerMarkupTags(); plugin authors copying the 'Class@method' convention from Laravel-ish contexts where October requires [Class::class, 'method'] or a closure; definitions supplied via YAML/JSON where the callable string was mangled.","solutions":["Fix the callable: use [MyHelper::class, 'method'], a namespaced function string like 'strtoupper', or a closure.","Verify the method exists, is public (and static if called statically), and the class is autoloaded with the correct namespace.","Replace 'Class@method' strings with the array form — PHP's is_callable() rejects them.","Sanity-check with is_callable($callback) right after registering before reloading the frontend."],"exampleFix":"// before\npublic function registerMarkupTags()\n{\n    return ['filters' => ['excerpt' => 'Acme\\Blog\\Classes\\Excerpt@make']];\n}\n\n// after\npublic function registerMarkupTags()\n{\n    return ['filters' => ['excerpt' => [\\Acme\\Blog\\Classes\\Excerpt::class, 'make']]];\n}","handlingStrategy":"type-guard","validationCode":"// Validate registerMarkupTags() output before returning it\npublic function registerMarkupTags()\n{\n    $tags = ['filters' => ['excerpt' => [Excerpt::class, 'make']]];\n\n    foreach ($tags['filters'] ?? [] as $name => $callback) {\n        if (!is_callable($callback)) {\n            throw new RuntimeException(\"Markup filter '{$name}' callback is not callable.\");\n        }\n    }\n\n    return $tags;\n}","typeGuard":"function isMarkupCallbackValid($callback): bool\n{\n    if (is_string($callback) && strpos($callback, '@') !== false) {\n        return false; // 'Class@method' form is never callable in PHP\n    }\n    return is_callable($callback);\n}","tryCatchPattern":null,"preventionTips":["Use [Class::class, 'method'] or closures for markup callbacks; never 'Class@method' strings.","Test plugin boot plus one template render in CI so a bad callback fails the build, not production.","After renaming helper classes, grep registerMarkupTags() implementations for stale references."],"tags":["php","twig","markup-extension","callback","plugin-development"],"backgroundTag":"uncallable-callback","analyzedSha":"b608633a7e8922487d91a8161499020121c3b3bf","analyzedAt":"2026-08-21T04:24:57.515Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}