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

Group 'routes' must be an array

Error message

Group 'routes' must be an array

What it means

For each entry under config['groups'] (processed by mountGroupFromConfig), the optional inner 'routes' key must be an array of route definitions. A missing key defaults to an empty list, but if the key is present with a non-array value (object, string, scalar), GroupRoutesMustBeArray is thrown.

Source

Thrown at phalcon/Mvc/Router.zep:2141

            let paths = groupData["paths"];
        }

        let group = new Group(paths);

        if isset groupData["prefix"] {
            group->setPrefix((string) groupData["prefix"]);
        }

        if isset groupData["hostname"] {
            group->setHostname((string) groupData["hostname"]);
        }

        if !fetch routes, groupData["routes"] {
            let routes = [];
        }

        if typeof routes !== "array" {
            throw new GroupRoutesMustBeArray();
        }

        for routeData in routes {
            if !fetch pattern, routeData["pattern"] {
                throw new MissingGroupRouteKey("pattern");
            }
            if !fetch routePaths, routeData["paths"] {
                throw new MissingGroupRouteKey("paths");
            }

            let method = "";
            if isset routeData["method"] && routeData["method"] !== null {
                let method = strtolower((string) routeData["method"]);
            }

            switch method {
                case "":
                case "connect":

View on GitHub (pinned to b7419de9cd)

Solutions

  1. Make group 'routes' a list: "groups": [{"prefix": "/admin", "routes": [{"pattern": ..., "paths": ...}]}]
  2. Decode config with json_decode($json, true) so nested objects become arrays
  3. Pre-validate group entries before loadFromConfig

Example fix

// before
{
    "groups": [
        {
            "prefix": "/admin",
            "routes": {
                "pattern": "/dashboard",
                "paths": {"controller": "dashboard"}
            }
        }
    ]
}

// after
{
    "groups": [
        {
            "prefix": "/admin",
            "routes": [
                {
                    "pattern": "/dashboard",
                    "paths": {"controller": "dashboard"}
                }
            ]
        }
    ]
}
Defensive patterns

Strategy: validation

Validate before calling

// validate group structure: each group is a map; its 'routes' (if present) is a list
foreach ($config['groups'] ?? [] as $i => $group) {
    if (!is_array($group)) {
        throw new InvalidArgumentException("groups[$i] must be an array");
    }
    if (isset($group['routes']) && !is_array($group['routes'])) {
        throw new InvalidArgumentException("groups[$i]['routes'] must be an array");
    }
}

$router->loadFromConfig($config);

Type guard

function groupRoutesAreValid(mixed $group): bool
{
    return is_array($group)
        && (!isset($group['routes']) || is_array($group['routes']));
}

Try / catch

try {
    $router->loadFromConfig($config);
} catch (\Phalcon\Mvc\Router\Exceptions\GroupRoutesMustBeArray $e) {
    $logger->error('Group config rejected: ' . $e->getMessage());
    throw $e;
}

Prevention

When it happens

Trigger: JSON group object where "routes": {...} decodes to stdClass (json_decode without true); a group with "routes": "see other file"; single route object instead of a list under a group.

Common situations: Non-associative json_decode of the whole config (outer arrays survive only if wrapped in []); manual YAML authoring nesting an object where a list is required; config merging that replaced the list.

Related errors


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