cakephp/cakephp · error · InvalidArgumentException
Need a valid Closure to connect routes.
Error message
Need a valid Closure to connect routes.
What it means
RouteBuilder::scope() requires a Closure to define nested routes. If neither the $params argument nor the third argument is a Closure (i.e. $callback stays null), the builder throws this InvalidArgumentException, because a scope without a callback has no routes to connect.
Solutions
- Pass a closure as the callback: $routes->scope('/api', function ($routes) { ... });
- If you intended options, pass them in the array argument and still provide the Closure last: scope('/api', ['param' => 1], function ($routes) {...}).
- Check for null callback variables before calling scope().
Example fix
// before
$routes->scope('/api', 'routes');
// after
$routes->scope('/api', function (RouteBuilder $routes) {
$routes->connect('/ping', ['controller' => 'Ping', 'action' => 'index']);
}); Defensive patterns
Strategy: type-guard
Validate before calling
if (!$callback instanceof \Closure) {
throw new InvalidArgumentException('scope() requires a Closure callback');
} Type guard
function isValidScopeCallback(mixed $cb): bool { return $cb instanceof \Closure; } Prevention
- Always pass an anonymous function as the last argument to scope().
- Keep argument order: path, params array (optional), Closure.
- Add static analysis (PHPStan/Psalm) rules to catch non-Closure callbacks.
When it happens
Trigger: $routes->scope('/api') with no closure; passing something other than a Closure (e.g. an array or a string) as the callback; transposing the $params and $callback arguments.
Common situations: Refactor stubs leaving scope() without a body; copy-paste from non-closure route APIs; dynamic code building scopes where the callback variable is conditionally null.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- Missing required route key
- Cannot apply ` ` middleware or middleware group. Use…
- Cannot find route class
- Invalid HTTP method received.
- Route ` ` expects the URL option `_entity` to be an array…
AI-assisted analysis of cakephp/cakephp@1128eba9b0 (2026-09-12).
Data as JSON: /api/errors/36bb9d38bcfb8afd.
Report an issue: GitHub.
Appendix: source
Thrown at src/Routing/RouteBuilder.php:959
*
* - `_namePrefix` Set a prefix used for named routes. The prefix is prepended to the
* name of any route created in a scope callback.
*
* @param string $path The path to create a scope for.
* @param \Closure|array $params Either the parameters to add to routes, or a callback.
* @param \Closure|null $callback The callback to invoke that builds the plugin routes.
* Only required when $params is defined.
* @return $this
* @throws \InvalidArgumentException when there is no callable parameter.
*/
public function scope(string $path, Closure|array $params, ?Closure $callback = null)
{
if ($params instanceof Closure) {
$callback = $params;
$params = [];
}
if ($callback === null) {
throw new InvalidArgumentException('Need a valid Closure to connect routes.');
}
if ($this->_path !== '/') {
$path = $this->_path . $path;
}
$namePrefix = $this->_namePrefix;
if (isset($params['_namePrefix'])) {
$namePrefix .= $params['_namePrefix'];
}
unset($params['_namePrefix']);
$params += $this->_params;
$builder = new static($this->_collection, $path, $params, [
'routeClass' => $this->_routeClass,
'extensions' => $this->_extensions,
'namePrefix' => $namePrefix,
'middleware' => $this->middleware,
]);View on GitHub (pinned to 1128eba9b0)