phalcon/cphalcon · error · Phalcon\Mvc\Router\Exceptions\InvalidCallbackParameter
The 'callback' parameter must be either a callable or NULL.
Error message
The 'callback' parameter must be either a callable or NULL.
What it means
AnnotationsRouter::setActionPreformatCallback() accepts only a valid callable (existing function name string, callable array, closure, or invokable object) or null (which installs a default uncamelize-with-dash callback). Any other value — false, an integer, a non-callable string, a plain object without __invoke — throws InvalidCallbackParameter.
Source
Thrown at phalcon/Mvc/Router/Annotations.zep:479
* // String as callback
* $annotationRouter->setActionPreformatCallback('strtolower');
*
* // If empty method constructor called [null], sets uncamelize with - delimiter
* $annotationRouter->setActionPreformatCallback();
* ```
*
* @param callable|string|null $callback
*/
public function setActionPreformatCallback(var callback = null) -> <self>
{
if likely is_callable(callback) {
let this->actionPreformatCallback = callback;
} elseif callback === null {
let this->actionPreformatCallback = function (action) {
return uncamelize(action, "-");
};
} else {
throw new InvalidCallbackParameter();
}
return this;
}
/**
* @return callable|string|null
*/
public function getActionPreformatCallback()
{
return this->actionPreformatCallback;
}
/**
* Changes the controller class suffix
*/
public function setControllerSuffix( string controllerSuffix) -> <self>
{View on GitHub (pinned to b7419de9cd)
Solutions
- Pass null to (re)install the default uncamelize behavior
- Pass a closure: $router->setActionPreformatCallback(fn ($action) => strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $action)));
- Verify is_callable($value) || $value === null before assigning values that come from config
Example fix
// before
$router->setActionPreformatCallback('my_preformat'); // function does not exist
$router->setActionPreformatCallback(false); // not callable, not null
// after
$router->setActionPreformatCallback(
function (string $action): string {
return uncamelize($action, "-");
}
); Defensive patterns
Strategy: validation
Validate before calling
// accept only null or verified callables from config before assignment
$callback = $config['actionPreformatCallback'] ?? null;
if ($callback !== null && !is_callable($callback)) {
throw new InvalidArgumentException(
'actionPreformatCallback must be callable or null, got ' . var_export($callback, true)
);
}
$router->setActionPreformatCallback($callback); Type guard
function isPreformatCallback(mixed $cb): bool
{
return $cb === null || is_callable($cb);
} Try / catch
try {
$router->setActionPreformatCallback($callback);
} catch (\Phalcon\Mvc\Router\Exceptions\InvalidCallbackParameter $e) {
$logger->error('Invalid action preformat callback: ' . get_debug_type($callback));
throw $e;
} Prevention
- Pass null to reset to the default uncamelize behavior — never false or ''
- Prefer closures so validity is checked by the engine at definition
- Treat config-supplied callback names as untrusted: verify is_callable() during load
When it happens
Trigger: setActionPreformatCallback('underscore_action') where no such function exists; passing false or '' expecting to reset; passing an object that lacks __invoke; a string naming a method without a valid 'Class::method' static form.
Common situations: Trying to disable the preformat step by passing false; copy-pasting a callback name from another project; passing a configured value from a settings file unchecked.
Related errors
- Before-Match callback is not callable in matched route '{pat
- Before-Match callback is not callable in matched route '{pat
- Wrong key in paths: {part}
- loadFromConfig requires an array or Phalcon\Config\ConfigInt
- 'defaults' must be an array
AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21).
Data as JSON: /api/errors/97a18b25b06e5060.
Report an issue: GitHub.