{"record":{"id":"731ff29f24513a86","repo":"phalcon/cphalcon","slug":"path-must-be-a-string-or-a-closure","errorCode":null,"errorMessage":"'path' must be a string or a closure","messagePattern":"'path' must be a string or a closure","errorType":"exception","errorClass":"InvalidPathType","httpStatus":null,"severity":"error","filePath":"phalcon/Mvc/View/Engine/Volt/Compiler.zep","lineNumber":460,"sourceCode":"                let compiledTemplatePath = compiledPath . prefix . templateSepPath . compiledExtension;\n            }\n        } elseif typeof compiledPath == \"object\" && compiledPath instanceof Closure {\n            /**\n             * A closure can dynamically compile the path\n             */\n            let compiledTemplatePath = call_user_func_array(\n                compiledPath,\n                [templatePath, options, extendsMode]\n            );\n\n            /**\n             * The closure must return a valid path\n             */\n            if unlikely typeof compiledTemplatePath != \"string\" {\n                throw new InvalidPathClosureReturn();\n            }\n        } else {\n            throw new InvalidPathType();\n        }\n\n        /**\n         * Compile always must be used only in the development stage\n         */\n        if !this->phpFileExists(compiledTemplatePath) || compileAlways {\n            /**\n             * The file needs to be compiled because it either does not exist or\n             * needs to compiled every time\n             */\n            let compilation = this->compileFile(\n                templatePath,\n                compiledTemplatePath,\n                extendsMode\n            );\n        } else {\n            if stat === true {\n                /**","sourceCodeStart":442,"sourceCodeEnd":478,"githubUrl":"https://github.com/phalcon/cphalcon/blob/b7419de9cd0a8a3f48441ead84c9f8415d463e25/phalcon/Mvc/View/Engine/Volt/Compiler.zep#L442-L478","documentation":"Thrown by Volt\\Compiler::compileFile()/compile() when the 'compiledPath' option is neither a string nor a Closure. Volt builds the compiled-template destination path from this option: a string is used as the directory prefix, a Closure is invoked with (templatePath, options, extendsMode) to compute it dynamically. Any other type (null, int, array, plain object) cannot be resolved into a destination, so compilation aborts before the template is read.","triggerScenarios":"Calling $compiler->compile('view.volt') or $volt->render() while the 'compiledPath' option is null, an array, an integer, or a non-Closure object. Typical when the Volt service is registered with a typo'd key (e.g. 'compiled_path') so compiledPath stays null, or when a config value that should be a string is loaded as another type.","commonSituations":"Misconfigured Volt service in the DI container (wrong option name, null from missing config entry), passing an array of options where the path string is expected, or a Closure import mismatch (a string class reference instead of an actual Closure instance).","solutions":["Set 'compiledPath' to a string directory, e.g. ['compiledPath' => appPath('/storage/cache/volt/')], or to a \\Closure returning a string","Verify the exact option names when registering the Volt service ('compiledPath', 'compiledSeparator', 'compiledExtension', 'stat', 'compileAlways')","If using a Closure, confirm it is a real \\Closure and that it returns a string (returning non-string throws the sibling InvalidPathClosureReturn)"],"exampleFix":"// before\n$volt->setOptions(['compiledPath' => null]); // or typo: 'compiled_path'\n\n// after\n$volt->setOptions([\n    'compiledPath' => appPath('/storage/cache/volt/'),\n    'compileAlways' => false,\n]);","handlingStrategy":"validation","validationCode":"$options = $volt->getOptions();\n$compiledPath = $options['compiledPath'] ?? null;\nif (!is_string($compiledPath) && !($compiledPath instanceof \\Closure)) {\n    throw new \\InvalidArgumentException(\n        \"Volt 'compiledPath' must be a string or Closure, got \" . gettype($compiledPath)\n    );\n}","typeGuard":"function isValidVoltCompiledPath(mixed $path): bool\n{\n    return is_string($path) || $path instanceof \\Closure;\n}","tryCatchPattern":null,"preventionTips":["Register the Volt service from one central factory so compiledPath is always set with the right type","Add a boot-time assertion on Volt options before the first render","Use is_string()/instanceof Closure checks when compiledPath comes from user/config input"],"tags":["volt","template","compiler","configuration","invalid-type"],"backgroundTag":"invalid-argument-type","analyzedSha":"b7419de9cd0a8a3f48441ead84c9f8415d463e25","analyzedAt":"2026-08-21T06:21:18.811Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}