phalcon/cphalcon · error · Phalcon\Mvc\Router\Exceptions\BeforeMatchNotCallable

Before-Match callback is not callable in matched route

Error message

Before-Match callback is not callable in matched route

What it means

During matching, when a static (literal-pattern) route has a beforeMatch callback attached, the router verifies it with is_callable() before invoking it. If the value is set but not callable - a string naming a nonexistent function/method, a malformed array callable, or a class that is not autoloadable at match time - BeforeMatchNotCallable is thrown while handling the request.

Source

Thrown at phalcon/Mvc/Router.zep:1353

                        let staticHostRegex = staticRoute->getCompiledHostName();

                        if staticHostRegex !== null {
                            let staticMatched = preg_match(staticHostRegex, currentHostName);
                        } else {
                            let staticMatched = currentHostName == staticHostname;
                        }

                        if !staticMatched {
                            continue;
                        }
                    }

                    let staticBeforeMatch = staticRoute->getBeforeMatch();

                    if staticBeforeMatch !== null {
                        if unlikely !is_callable(staticBeforeMatch) {
                            throw new BeforeMatchNotCallable();
                        }

                        let routeFound = {staticBeforeMatch}(handledUri, staticRoute, this);

                        if !routeFound {
                            continue;
                        }
                    }

                    let routeFound         = true,
                        matches            = null,
                        parts              = staticRoute->getPaths(),
                        this->matchedRoute = staticRoute;

                    break;
                }
            }
        }

View on GitHub (pinned to b7419de9cd)

Solutions

  1. Fix the reference so it points at an existing, autoloadable static method: beforeMatch('App\Filters\AdminGate::check')
  2. Verify with is_callable() at route-definition time (assert once during boot) so bad references fail at startup with your own context
  3. Regenerate the router cache after renaming/moving any class referenced by beforeMatch callables
  4. Ensure the autoloader covering the callable's class is active in every process that loads the cached routes (web + CLI)

Example fix

// before
$router->add('/admin', ['controller' => 'admin', 'action' => 'index'])
       ->beforeMatch('App\Filter\AdminGate::check'); // wrong namespace -> throws at match time

// after: assert at definition time, reference a real method
class AdminGate { public static function check(string $uri, \Phalcon\Mvc\Router\RouteInterface $route): bool { /* ... */ return true; } }
$cb = [AdminGate::class, 'check'];
assert(is_callable($cb));
$router->add('/admin', ['controller' => 'admin', 'action' => 'index'])
       ->beforeMatch($cb);
Defensive patterns

Strategy: validation

Validate before calling

// Assert every beforeMatch is really callable at definition time
foreach ($router->getRoutes() as $route) {
    $cb = $route->getBeforeMatch();
    if ($cb !== null && !is_callable($cb)) {
        throw new LogicException('Route ' . $route->getRouteId() . ' has a non-callable beforeMatch');
    }
}

Type guard

function isUsableBeforeMatch(mixed $cb): bool
{
    return $cb === null || is_callable($cb);
}

Try / catch

try {
    $router->handle();
} catch (\Phalcon\Mvc\Router\Exceptions\BeforeMatchNotCallable $e) {
    // one route's guard references a missing class/method: fail loud, page 500 with context
    $logger->critical('beforeMatch not callable: ' . $e->getMessage());
    throw $e;
}

Prevention

When it happens

Trigger: ->beforeMatch('App\Filters\Gate::check') where Gate::check does not exist (renamed/removed, typo, wrong namespace); array callable ['Filter', 'method'] where the method is not static or is misspelled; loading routes from a cache dump into a process whose autoloader cannot find the filter class (cache built in CLI, matched in web with a different autoload path).

Common situations: Renaming or deleting a filter class without regenerating route caches; typos in callable strings; non-public or instance methods referenced as static callables; composer autoload differences between the dumping environment and the serving environment.

Related errors


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