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

'groups' must be an array

Error message

'groups' must be an array

What it means

In loadFromConfig(), the optional 'groups' key must be an array of group definition arrays (each may carry 'prefix', 'hostname', 'routes', and per-route entries). If config['groups'] exists but is not an array, ConfigKeyMustBeArray('groups') is thrown before any group is mounted.

Source

Thrown at phalcon/Mvc/Router.zep:1784

            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);
        }

        return this;
    }

    /**
     * Mounts a group of routes in the router
     *
     * @param GroupInterface group
     *

View on GitHub (pinned to b7419de9cd)

Solutions

  1. Wrap each group in a list: "groups": [{"prefix": "/admin", "routes": [...]}, ...]
  2. Use json_decode($json, true) so nested group objects arrive as arrays
  3. Pre-validate the 'groups' key is a list before calling loadFromConfig

Example fix

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

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

Strategy: validation

Validate before calling

// 'groups' must be a list of group definitions, each with an array 'routes' (optional)
$groups = $config['groups'] ?? [];
if (!is_array($groups)) {
    throw new InvalidArgumentException("'groups' must be an array of group definitions");
}
foreach ($groups as $i => $group) {
    if (!is_array($group) || (isset($group['routes']) && !is_array($group['routes']))) {
        throw new InvalidArgumentException("groups[$i] invalid: 'routes' must be an array");
    }
}

$router->loadFromConfig($config);

Type guard

function groupsAreValid(array $config): bool
{
    if (!isset($config['groups'])) {
        return true;
    }
    if (!is_array($config['groups'])) {
        return false;
    }
    foreach ($config['groups'] as $group) {
        if (!is_array($group) || (isset($group['routes']) && !is_array($group['routes']))) {
            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

When it happens

Trigger: "groups": {"prefix": "/admin", "routes": [...} (a single group object instead of a list of groups); groups decoded to stdClass by json_decode without true; groups defined as a comma-separated string.

Common situations: Same JSON object-vs-array decode issue as 'routes'; defining one group inline without the enclosing [ ]; config merges that overwrite the groups list with a non-array.

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


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