phalcon/cphalcon · error · Phalcon\Session\Adapter\Exceptions\SavePathUnavailable

The session save path [{path}] is not writable

Error message

The session save path [{path}] is not writable

What it means

SavePathUnavailable is thrown by the Session\Adapter\Stream constructor when the resolved save path exists in configuration but PHP's is_writable() check fails. The process running PHP (www-data, apache, cli user) has no write permission on the directory that holds session files.

Source

Thrown at phalcon/Session/Adapter/Stream.zep:98

    public function __construct( array options = [])
    {
        var path;

        let this->prefix  = this->getArrVal(options, "prefix", ""),
            this->options = options;

        /**
         * Get the save_path from the passed options. If not defined
         * get it from php.ini
         */
        let path = this->getArrVal(options, "savePath", this->phpIniGet("session.save_path"));

        if unlikely true === empty(path) {
            throw new InvalidSavePath();
        }

        if unlikely true !== this->phpIsWritable(path) {
            throw new SavePathUnavailable(path);
        }

        let this->path = this->toDirSeparator(path);
    }

    public function destroy(string id) -> bool
    {
        var file;

        let file = this->path . this->getPrefixedName(id);

        if this->phpFileExists(file) && is_file(file) {
            this->phpUnlink(file);
        }

        return true;
    }

View on GitHub (pinned to b7419de9cd)

Solutions

  1. Fix ownership and mode for the PHP user: chown -R www-data:www-data /var/lib/app/sessions && chmod 770 /var/lib/app/sessions
  2. Verify from the app user's perspective: sudo -u www-data test -w /var/lib/app/sessions, or php -r 'var_dump(is_writable($dir));'
  3. Use a directory guaranteed writable for local work, e.g. sys_get_temp_dir()
  4. Check open_basedir in php.ini / pool config covers the chosen path

Example fix

// before
new Stream(['savePath' => '/var/lib/myapp/sessions']); // SavePathUnavailable: not writable

// after (dir owned by the FPM user, writable)
// shell: chown www-data:www-data /var/lib/myapp/sessions && chmod 770 /var/lib/myapp/sessions
new Stream(['savePath' => '/var/lib/myapp/sessions']);
Defensive patterns

Strategy: validation

Validate before calling

$path = $options['savePath'] ?? ini_get('session.save_path');
if (!is_dir($path)) {
    @mkdir($path, 0770, true);
}
if (!is_writable($path)) {
    throw new RuntimeException("Session save path {$path} is not writable by " . get_current_user());
}

Prevention

When it happens

Trigger: savePath points to a directory owned by root while PHP-FPM runs as www-data; directory mode 755 with no write for the PHP user; open_basedir excludes the path; SELinux denies writes; the path is a read-only volume in a container.

Common situations: Manually created session directory with wrong ownership; switching from mod_php (user apache) to PHP-FPM (user www-data) without chown-ing the session directory; hardened shared hosting with open_basedir; systemd PrivateTmp hiding the expected /tmp to other services.

Related errors


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