phalcon/cphalcon · error · Phalcon\Cli\Router\Exceptions\BeforeMatchNotCallable
Before-Match callback is not callable in matched route '{pat
Error message
Before-Match callback is not callable in matched route '{pattern}' What it means
Defensive recheck at match time: once a route matches and has a beforeMatch callback, the router runs is_callable() on it before invoking it with (arguments, route, router). Route::beforeMatch() already rejects non-callables at registration, so this variant fires when the callback becomes non-callable after the route was built.
Source
Thrown at phalcon/Cli/Router.zep:265
if memstr(pattern, "^") {
let routeFound = preg_match(pattern, arguments, matches);
} else {
let routeFound = pattern == arguments;
}
/**
* Check for beforeMatch conditions
*/
if routeFound {
let beforeMatch = route->getBeforeMatch();
if beforeMatch !== null {
/**
* Check first if the callback is callable
*/
if unlikely !is_callable(beforeMatch) {
throw new BeforeMatchNotCallable(route->getPattern());
}
/**
* Check first if the callback is callable
*/
let routeFound = call_user_func_array(
beforeMatch,
[
arguments,
route,
this
]
);
}
}
if routeFound {
/**View on GitHub (pinned to b7419de9cd)
Solutions
- Rebuild routes on each run instead of serializing them with beforeMatch closures
- Ensure the callback target (function name or [object, method] pair) still exists at match time
- If subclassing Route, keep getBeforeMatch() returning a valid callable or null
Example fix
// before (cached/serialized routes)
$routes = unserialize($cache->get('cli-routes'));
$router = new Router();
// beforeMatch closures were lost on serialize -> match-time throw
// after
$router = new Router();
$router->add('/backup', ['task' => 'backup'])
->beforeMatch(function ($args, $route, $router) {
return php_sapi_name() === 'cli';
}); Defensive patterns
Strategy: validation
Validate before calling
// Before matching, assert every route's beforeMatch survived registration/cache
foreach ($router->getRoutes() as $route) {
$beforeMatch = $route->getBeforeMatch();
if ($beforeMatch !== null && !is_callable($beforeMatch)) {
throw new RuntimeException(
'Route ' . $route->getPattern() . ' has a non-callable beforeMatch'
);
}
}
$router->handle($_SERVER['argv'] ?? null); Try / catch
try {
$router->handle($arguments);
} catch (\Phalcon\Cli\Router\Exception\BeforeMatchNotCallable $e) {
// Callback degraded after registration (cache/serialization/subclass).
// Rebuild the route table without the cached beforeMatch and retry.
rebuildRoutes($router);
$router->handle($arguments);
} Prevention
- Do not serialize/cache routes that carry closure beforeMatch callbacks — rebuild routes each run
- If subclassing Route, keep getBeforeMatch() returning a valid callable or null
- Add a smoke test that matches one route per module so callback regressions surface in CI
When it happens
Trigger: A custom Route subclass that sets the beforeMatch property directly or overrides getBeforeMatch(); routes restored from cache/serialization where a closure was lost to a string or the referenced method no longer exists.
Common situations: Caching or serializing route objects with closures (closures do not survive serialization); renaming/removing the referenced function or method after routes were registered; relying on __call magic for which is_callable returns false.
Related errors
- Before-Match callback is not callable in matched route '{pat
- Arguments must be an array or string, {type} given
- The route contains invalid paths ('{pattern}')
- The 'callback' parameter must be either a callable or NULL.
- Invalid module definition for module '{moduleName}': The mod
AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21).
Data as JSON: /api/errors/66368589bba4f79a.
Report an issue: GitHub.