{"record":{"id":"25bd3732ddacee86","repo":"phalcon/cphalcon","slug":"views-directory-item-must-be-a-string","errorCode":null,"errorMessage":"Views directory item must be a string","messagePattern":"Views directory item must be a string","errorType":"exception","errorClass":"Phalcon\\Mvc\\View\\Exceptions\\ViewsDirItemMustBeString","httpStatus":null,"severity":"error","filePath":"phalcon/Mvc/View.zep","lineNumber":1135,"sourceCode":"     * Sets the views directory. Depending of your platform,\n     * always add a trailing slash or backslash\n     */\n    public function setViewsDir(var viewsDir) -> <static>\n    {\n        var position, directory, newViewsDir;\n\n        if typeof viewsDir !== \"string\" && typeof viewsDir !== \"array\" {\n            throw new InvalidViewsDirType();\n        }\n\n        if typeof viewsDir == \"string\" {\n            let this->viewsDirs = this->toDirSeparator(viewsDir);\n        } else {\n            let newViewsDir = [];\n\n            for position, directory in viewsDir {\n                if typeof directory != \"string\" {\n                    throw new ViewsDirItemMustBeString();\n                }\n\n                let newViewsDir[position] = this->toDirSeparator(directory);\n            }\n\n            let this->viewsDirs = newViewsDir;\n        }\n\n        return this;\n    }\n\n    /**\n     * Starts rendering process enabling the output buffering\n     */\n    public function start() -> <static>\n    {\n        ob_start();\n","sourceCodeStart":1117,"sourceCodeEnd":1153,"githubUrl":"https://github.com/phalcon/cphalcon/blob/b7419de9cd0a8a3f48441ead84c9f8415d463e25/phalcon/Mvc/View.zep#L1117-L1153","documentation":"When setViewsDir() receives an array, Phalcon iterates it and each element must itself be a string path; a non-string element (null, nested array, int) triggers ViewsDirItemMustBeString at phalcon/Mvc/View.zep:1135. The array form exists to let getViewsDirs()/render check multiple view roots in order (e.g. theme dir + app dir), so every entry must be a usable path.","triggerScenarios":"setViewsDir([$themesDir . '/dark', null]) where one slot is an unset variable; setViewsDir(json_decode($json, true)) where a value decoded as int/array; array_merge producing nested arrays.","commonSituations":"Multi-theme setups assembling directory lists dynamically; config arrays with optional directories that end up null; a views array built from a JSON/YAML source with mixed types.","solutions":["array_filter($dirs, 'is_string') (or filter out nulls) before passing the array","Default optional entries to '' or omit them entirely","Validate each entry with is_string() when building the list dynamically"],"exampleFix":"// before\n$view->setViewsDir([$config->themePath, $config->fallbackThemePath]); // fallback null\n\n// after\n$dirs = array_filter([$config->themePath, $config->fallbackThemePath], 'is_string');\n$view->setViewsDir(array_values($dirs));","handlingStrategy":"validation","validationCode":"$dirs = array_values(array_filter($dirs, 'is_string'));\nif ($dirs === []) {\n    throw new \\InvalidArgumentException('At least one string views directory is required');\n}","typeGuard":"function isStringList(mixed $list): bool\n{\n    return is_array($list) && array_reduce($list, fn($ok, $v) => $ok && is_string($v), true);\n}","tryCatchPattern":null,"preventionTips":["Filter optional directory entries before setViewsDir()","Keep multi-dir lists flat — no nested arrays","Build views-dir lists from typed config schemas"],"tags":["phalcon","view","paths","array-validation"],"backgroundTag":"config-type-mismatch","analyzedSha":"b7419de9cd0a8a3f48441ead84c9f8415d463e25","analyzedAt":"2026-08-21T06:21:18.811Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}