phalcon/cphalcon · error · Phalcon\Mvc\Router\Exceptions\ConfigKeyMustBeArray
'routes' must be an array
Error message
'routes' must be an array
What it means
In loadFromConfig(), the 'routes' key must be an array of route definition arrays (each with at least 'pattern' and 'paths'). If config['routes'] exists but is anything other than an array, ConfigKeyMustBeArray('routes') is thrown before any route is processed.
Source
Thrown at phalcon/Mvc/Router.zep:1775
throw new InvalidConfigSource();
}
if isset config["removeExtraSlashes"] {
let removeExtra = config["removeExtraSlashes"];
this->removeExtraSlashes((bool) removeExtra);
}
if isset config["defaults"] {
let defaults = config["defaults"];
if typeof defaults !== "array" {
throw new ConfigKeyMustBeArray("defaults");
}
this->setDefaults(defaults);
}
if fetch routes, config["routes"] {
if typeof routes !== "array" {
throw new ConfigKeyMustBeArray("routes");
}
for routeData in routes {
this->addRouteFromConfig(routeData);
}
}
if fetch groups, config["groups"] {
if typeof groups !== "array" {
throw new ConfigKeyMustBeArray("groups");
}
for groupData in groups {
this->mountGroupFromConfig(groupData);
}
}
if fetch notFoundPaths, config["notFound"] {
this->notFound(notFoundPaths);
}View on GitHub (pinned to b7419de9cd)
Solutions
- Wrap route entries in a list: "routes": [{"pattern": "/api", "paths": {...}}, ...]
- Decode JSON with json_decode($json, true) so objects inside 'routes' become arrays too
- Pre-validate the config shape (see validation snippet) and report the offending file/line
Example fix
// before
{
"routes": {
"pattern": "/api/users",
"paths": {"controller": "users"}
}
}
// after
{
"routes": [
{
"pattern": "/api/users",
"paths": {"controller": "users"}
}
]
} Defensive patterns
Strategy: validation
Validate before calling
// every entry under 'routes' must itself be an array containing pattern + paths
$routes = $config['routes'] ?? [];
if (!is_array($routes)) {
throw new InvalidArgumentException("'routes' must be an array of route definitions");
}
foreach ($routes as $i => $entry) {
if (!is_array($entry) || !isset($entry['pattern'], $entry['paths'])) {
throw new InvalidArgumentException("routes[$i] must contain 'pattern' and 'paths'");
}
}
$router->loadFromConfig($config); Type guard
function routesAreValid(array $config): bool
{
if (!isset($config['routes'])) {
return true;
}
if (!is_array($config['routes'])) {
return false;
}
foreach ($config['routes'] as $entry) {
if (!is_array($entry) || !isset($entry['pattern'], $entry['paths'])) {
return false;
}
}
return true;
} Try / catch
try {
$router->loadFromConfig($config);
} catch (\Phalcon\Mvc\Router\Exceptions\ConfigKeyMustBeArray $e) {
$logger->error('Router config rejected: ' . $e->getMessage());
throw $e;
} Prevention
- Wrap every route entry in [ { ... } ] — one object per list element
- Use associative decoding for JSON and sequence syntax (dashed lists) in YAML
- Validate the routes file schema before deploy, not at request time
When it happens
Trigger: "routes": {...} decoded as a JSON object; a single route definition passed directly under 'routes' instead of wrapped in a list; routes serialized as a string.
Common situations: json_decode without associative flag; writing a single route object under 'routes' ({"pattern": ..., "paths": ...}) instead of a one-element list ([{...}]); merging configs where a later override replaced the list with a scalar.
Understand the failure class
Background: Config validation failed: what "invalid value for {key}" and settings-rejection errors mean across 19 open-source libraries — this error's family across 19 libraries.
Related errors
- loadFromConfig requires an array or Phalcon\Config\ConfigInt
- 'defaults' must be an array
- 'groups' must be an array
- Route config entry is missing 'pattern'
- Route config entry is missing 'paths'
AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21).
Data as JSON: /api/errors/6c988a63e478df23.
Report an issue: GitHub.