phalcon/cphalcon · error · Phalcon\Mvc\View\Engine\Volt\Exceptions\InvalidPathClosureReturn

'path' closure didn't return a valid string

Error message

'path' closure didn't return a valid string

What it means

The Volt 'path' option may be a directory string or a closure invoked as (templatePath, options, extendsMode) to compute the compiled-file location dynamically; the closure's return value must be a string, and InvalidPathClosureReturn is thrown when it is not (phalcon/Mvc/View/Engine/Volt/Compiler.zep:457) — e.g. null from a missing return, void functions, or accidental array returns. (Its sibling InvalidPathType covers a 'path' option that is neither string nor closure.)

Source

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

            if extendsMode {
                let compiledTemplatePath = compiledPath . prefix . templateSepPath . compiledSeparator . "e" . compiledSeparator . compiledExtension;
            } else {
                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
            );

View on GitHub (pinned to b7419de9cd)

Solutions

  1. Make every branch of the closure return a string (absolute path of the compiled file)
  2. Ensure parent directories exist inside the closure (mkdir recursive) and still return the path string
  3. Add a return-type hint or assertion: $path = ...; assert(is_string($path)); return $path;

Example fix

// before
$volt->setOptions(['path' => function ($templatePath) {
    if (!is_dir($dir)) { mkdir($dir, 0777, true); } // bool returned when branch taken
}]);

// after
$volt->setOptions(['path' => function ($templatePath) {
    $dir = cache_path('volt/');
    if (!is_dir($dir)) { mkdir($dir, 0777, true); }
    return $dir . md5($templatePath) . '.php';
}]);
Defensive patterns

Strategy: validation

Validate before calling

$pathClosure = function (string $templatePath, array $options, bool $extendsMode): string {
    $dir = cache_path('volt/');
    if (!is_dir($dir)) { mkdir($dir, 0777, true); }
    return $dir . md5($templatePath) . '.php';
};
$result = $pathClosure('x.phtml', [], false);
if (!is_string($result) || $result === '') {
    throw new \LogicException('Volt path closure must return a non-empty string');
}

Try / catch

use Phalcon\Mvc\View\Engine\Volt\Exceptions\InvalidPathClosureReturn;
// The throw happens at compile time inside Volt; catch around rendering:
try {
    $view->render('page/index');
} catch (InvalidPathClosureReturn $e) {
    $logger->error('Volt path closure must return a string path');
    throw $e;
}

Prevention

When it happens

Trigger: A path closure that builds a path conditionally and forgets a return branch; returning the result of mkdir() (bool) or an array from a helper; using a function with side effects that returns void; returning null when a hash function fails.

Common situations: Per-tenant or hashed compiled paths computed in a closure; refactoring the closure to call a service whose method returns void; debugging code left in the closure that echoes instead of returns.

Related errors


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