phalcon/cphalcon · error · InvalidCompilationPrefix

The unique compilation prefix is invalid

Error message

The unique compilation prefix is invalid

What it means

Volt's compilation prefix (prepended to compiled template keys/names, e.g. for compiled-file naming) is set with Compiler::setPrefix() and may be a string or a Closure receiving the compiler. At compile time Compiler.zep:2137 resolves that Closure and then requires the final value to be a string. If the prefix is neither a string nor a Closure returning one (integer, null, array), the 'Invalid compilation prefix' exception is thrown before any template is compiled.

Source

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

            let this->prefix = unique_path_key(this->currentPath);
        }

        /**
         * The user could use a closure generator
         */
        if typeof this->prefix == "object" {
            if this->prefix instanceof Closure {
                let this->prefix = call_user_func_array(
                    this->prefix,
                    [
                        this
                    ]
                );
            }
        }

        if unlikely typeof this->prefix != "string" {
            throw new InvalidCompilationPrefix();
        }

        return this->prefix;
    }


    /**
     * Parses a Volt template returning its intermediate representation
     *
     *```php
     * print_r(
     *     $compiler->parse("{{ 3 + 2 }}")
     * );
     *```
     *
     * @param string viewCode
     *
     * @return array

View on GitHub (pinned to b7419de9cd)

Solutions

  1. Make the Closure always return a string: setPrefix(function ($compiler) { return 'myapp'; })
  2. If the prefix comes from config, default it before assigning: $compiler->setPrefix($config->path('volt.prefix', 'default'))
  3. Pass a plain string literal if no dynamism is needed
  4. Add a return-type check in the Closure before returning

Example fix

// before
$compiler->setPrefix(function ($compiler) {
    if ($this->tenant) {
        return $this->tenant;
    }
});

// after
$compiler->setPrefix(function ($compiler) {
    return $this->tenant ?: 'default';
});
Defensive patterns

Strategy: validation

Validate before calling

if (!$prefix instanceof \Closure && !is_string($prefix)) {
    throw new \InvalidArgumentException(
        'Volt prefix must be a string or a Closure returning a string'
    );
}
if ($prefix instanceof \Closure) {
    $result = $prefix($compiler);
    if (!is_string($result)) {
        throw new \InvalidArgumentException(
            'Volt prefix Closure must return a string, got ' . gettype($result)
        );
    }
}
$compiler->setPrefix($prefix);

Prevention

When it happens

Trigger: $compiler->setPrefix(123), setPrefix(null), or setPrefix(function ($compiler) { return null; }) (a Closure with a code path that forgets to return), followed by any compile()/render() call.

Common situations: Code written for a dynamic per-application prefix whose Closure returns void on an early-exit branch. Prefixes read from config/env (e.g. getenv() returning false, or a missing config key yielding null) and passed to setPrefix unvalidated. Upgrading code that previously stored the prefix untyped.

Related errors


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