{"record":{"id":"b4e415fd00f2fbcd","repo":"phalcon/cphalcon","slug":"defaults-must-be-an-array","errorCode":null,"errorMessage":"'defaults' must be an array","messagePattern":"'defaults' must be an array","errorType":"exception","errorClass":"Phalcon\\Mvc\\Router\\Exceptions\\ConfigKeyMustBeArray","httpStatus":null,"severity":"error","filePath":"phalcon/Mvc/Router.zep","lineNumber":1768,"sourceCode":"            if !(config instanceof ConfigInterface) {\n                throw new InvalidConfigSource();\n            }\n            let config = config->toArray();\n        }\n\n        if typeof config !== \"array\" {\n            throw new InvalidConfigSource();\n        }\n\n        if isset config[\"removeExtraSlashes\"] {\n            let removeExtra = config[\"removeExtraSlashes\"];\n            this->removeExtraSlashes((bool) removeExtra);\n        }\n\n        if isset config[\"defaults\"] {\n            let defaults = config[\"defaults\"];\n            if typeof defaults !== \"array\" {\n                throw new ConfigKeyMustBeArray(\"defaults\");\n            }\n            this->setDefaults(defaults);\n        }\n\n        if fetch routes, config[\"routes\"] {\n            if typeof routes !== \"array\" {\n                throw new ConfigKeyMustBeArray(\"routes\");\n            }\n            for routeData in routes {\n                this->addRouteFromConfig(routeData);\n            }\n        }\n\n        if fetch groups, config[\"groups\"] {\n            if typeof groups !== \"array\" {\n                throw new ConfigKeyMustBeArray(\"groups\");\n            }\n            for groupData in groups {","sourceCodeStart":1750,"sourceCodeEnd":1786,"githubUrl":"https://github.com/phalcon/cphalcon/blob/b7419de9cd0a8a3f48441ead84c9f8415d463e25/phalcon/Mvc/Router.zep#L1750-L1786","documentation":"Inside loadFromConfig(), the optional 'defaults' key sets router defaults via setDefaults(), which requires an array (e.g. ['module' => 'frontend', 'controller' => 'index']). If config['defaults'] exists but is a scalar, string, or object, the router throws ConfigKeyMustBeArray('defaults') naming the offending key.","triggerScenarios":"A config file containing \"defaults\": \"frontend\" or \"defaults\": {\"module\": \"frontend\"} decoded to an object (json_decode without true) instead of an array; a YAML defaults scalar.","commonSituations":"json_decode() without the $associative flag turning every nested object into stdClass; hand-edited YAML using a scalar where a map was intended; defaults written as a single string module name.","solutions":["Make 'defaults' an array: \"defaults\": {\"module\": \"frontend\", \"controller\": \"index\", \"action\": \"index\"}","If decoding JSON, pass true: json_decode($json, true) so nested objects become arrays","Validate the shape before loading (see validation snippet) so the failure names the file, not the router"],"exampleFix":"// before\n$config = json_decode(file_get_contents('routes.json'));\n// {\"defaults\": \"frontend\"} -> stdClass/string defaults\n$router->loadFromConfig($config);\n\n// after\n$config = json_decode(file_get_contents('routes.json'), true);\n// {\"defaults\": {\"module\": \"frontend\", \"controller\": \"index\"}}\n$router->loadFromConfig($config);","handlingStrategy":"validation","validationCode":"// validate the whole router-config shape before handing it to the router\nfunction validateRouterConfig(array $config): void\n{\n    if (isset($config['defaults']) && !is_array($config['defaults'])) {\n        throw new InvalidArgumentException(\"'defaults' must be an array\");\n    }\n    if (isset($config['routes']) && !is_array($config['routes'])) {\n        throw new InvalidArgumentException(\"'routes' must be an array\");\n    }\n    if (isset($config['groups']) && !is_array($config['groups'])) {\n        throw new InvalidArgumentException(\"'groups' must be an array\");\n    }\n}\n\nvalidateRouterConfig($config);\n$router->loadFromConfig($config);","typeGuard":"function defaultsAreValid(array $config): bool\n{\n    return !isset($config['defaults']) || is_array($config['defaults']);\n}","tryCatchPattern":"try {\n    $router->loadFromConfig($config);\n} catch (\\Phalcon\\Mvc\\Router\\Exceptions\\ConfigKeyMustBeArray $e) {\n    // message names the offending key, e.g. \"'defaults' must be an array\"\n    $logger->error('Router config key invalid: ' . $e->getMessage());\n    throw $e;\n}","preventionTips":["Always call json_decode($json, true) for route configs so nested objects become arrays","Keep a JSON schema for your routes file and validate it in CI","Write 'defaults' as a map of module/controller/action, never a scalar"],"tags":["phalcon","router","config","defaults","validation"],"backgroundTag":"config-validation-failed","analyzedSha":"b7419de9cd0a8a3f48441ead84c9f8415d463e25","analyzedAt":"2026-08-21T06:21:18.811Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}