phalcon/cphalcon · error · InvalidPathType

'path' must be a string or a closure

Error message

'path' must be a string or a closure

What it means

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.

Source

Thrown at phalcon/Mvc/View/Engine/Volt/Compiler.zep:460

                let compiledTemplatePath = compiledPath . prefix . templateSepPath . compiledExtension;
            }
        } elseif typeof compiledPath == "object" && compiledPath instanceof Closure {
            /**
             * A closure can dynamically compile the path
             */
            let compiledTemplatePath = call_user_func_array(
                compiledPath,
                [templatePath, options, extendsMode]
            );

            /**
             * The closure must return a valid path
             */
            if unlikely typeof compiledTemplatePath != "string" {
                throw new InvalidPathClosureReturn();
            }
        } else {
            throw new InvalidPathType();
        }

        /**
         * Compile always must be used only in the development stage
         */
        if !this->phpFileExists(compiledTemplatePath) || compileAlways {
            /**
             * The file needs to be compiled because it either does not exist or
             * needs to compiled every time
             */
            let compilation = this->compileFile(
                templatePath,
                compiledTemplatePath,
                extendsMode
            );
        } else {
            if stat === true {
                /**

View on GitHub (pinned to b7419de9cd)

Solutions

  1. Set 'compiledPath' to a string directory, e.g. ['compiledPath' => appPath('/storage/cache/volt/')], or to a \Closure returning a string
  2. Verify the exact option names when registering the Volt service ('compiledPath', 'compiledSeparator', 'compiledExtension', 'stat', 'compileAlways')
  3. If using a Closure, confirm it is a real \Closure and that it returns a string (returning non-string throws the sibling InvalidPathClosureReturn)

Example fix

// before
$volt->setOptions(['compiledPath' => null]); // or typo: 'compiled_path'

// after
$volt->setOptions([
    'compiledPath' => appPath('/storage/cache/volt/'),
    'compileAlways' => false,
]);
Defensive patterns

Strategy: validation

Validate before calling

$options = $volt->getOptions();
$compiledPath = $options['compiledPath'] ?? null;
if (!is_string($compiledPath) && !($compiledPath instanceof \Closure)) {
    throw new \InvalidArgumentException(
        "Volt 'compiledPath' must be a string or Closure, got " . gettype($compiledPath)
    );
}

Type guard

function isValidVoltCompiledPath(mixed $path): bool
{
    return is_string($path) || $path instanceof \Closure;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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).

Understand the failure class

Background: Invalid argument type errors: "must be of type string", "expected X, got Y", and ERR_INVALID_ARG_TYPE explained — this error's family across 15 libraries.

Related errors


AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21). Data as JSON: /api/errors/731ff29f24513a86. Report an issue: GitHub.