{"record":{"id":"23a7b809d681f2a7","repo":"phalcon/cphalcon","slug":"the-session-save-path-cannot-be-empty","errorCode":null,"errorMessage":"The session save path cannot be empty","messagePattern":"The session save path cannot be empty","errorType":"exception","errorClass":"Phalcon\\Session\\Adapter\\Exceptions\\InvalidSavePath","httpStatus":null,"severity":"error","filePath":"phalcon/Session/Adapter/Stream.zep","lineNumber":94,"sourceCode":"     *\n     * @throws InvalidSavePath\n     * @throws SavePathUnavailable\n     */\n    public function __construct( array options = [])\n    {\n        var path;\n\n        let this->prefix  = this->getArrVal(options, \"prefix\", \"\"),\n            this->options = options;\n\n        /**\n         * Get the save_path from the passed options. If not defined\n         * get it from php.ini\n         */\n        let path = this->getArrVal(options, \"savePath\", this->phpIniGet(\"session.save_path\"));\n\n        if unlikely true === empty(path) {\n            throw new InvalidSavePath();\n        }\n\n        if unlikely true !== this->phpIsWritable(path) {\n            throw new SavePathUnavailable(path);\n        }\n\n        let this->path = this->toDirSeparator(path);\n    }\n\n    public function destroy(string id) -> bool\n    {\n        var file;\n\n        let file = this->path . this->getPrefixedName(id);\n\n        if this->phpFileExists(file) && is_file(file) {\n            this->phpUnlink(file);\n        }","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/phalcon/cphalcon/blob/b7419de9cd0a8a3f48441ead84c9f8415d463e25/phalcon/Session/Adapter/Stream.zep#L76-L112","documentation":"Phalcon\\Session\\Adapter\\Stream (the file-based session handler) resolves its storage directory in the constructor: it takes options['savePath'] and falls back to the session.save_path php.ini directive. InvalidSavePath is thrown when both resolve to an empty value, so the adapter has no directory in which to create session files.","triggerScenarios":"new Stream() or new Stream(['prefix' => 'x']) with no 'savePath' option while php.ini/CLI has no session.save_path set; PHP-FPM pools commonly ship with session.save_path empty, as do CLI scripts, containers and fresh minimal PHP installs.","commonSituations":"Fresh Docker/CLI/FPM environments where session.save_path was never configured; deploying to a new server after the adapter worked locally; a php.ini that sets session.save_path but the pool overrides it with an empty value.","solutions":["Pass an explicit savePath option: new Stream(['savePath' => '/tmp/phalcon-sessions'])","Set session.save_path in php.ini (e.g. session.save_path = '/var/lib/php/sessions') and restart PHP-FPM","If php.ini is not editable, call ini_set('session.save_path', $dir) before constructing the adapter","Add a bootstrap assertion on ini_get('session.save_path') so misconfigured environments fail at deploy, not at first request"],"exampleFix":"// before\n$session->setAdapter(new Stream()); // InvalidSavePath when session.save_path is empty\n\n// after\n$session->setAdapter(new Stream(['savePath' => '/tmp/phalcon-sessions']));","handlingStrategy":"validation","validationCode":"$savePath = $options['savePath'] ?? ini_get('session.save_path');\nif ($savePath === false || $savePath === '') {\n    throw new RuntimeException(\n        'No session save path: pass Stream option \\'savePath\\' or set session.save_path'\n    );\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pass an explicit 'savePath' option; treat php.ini as a fallback only","Add a deploy-time check that ini_get('session.save_path') is non-empty in environments relying on it","Cover session boot in smoke tests run under CLI and FPM, since defaults differ per SAPI"],"tags":["php","phalcon","session","configuration","filesystem"],"backgroundTag":"missing-config-path","analyzedSha":"b7419de9cd0a8a3f48441ead84c9f8415d463e25","analyzedAt":"2026-08-21T06:21:18.811Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}