phalcon/cphalcon · error · Phalcon\Session\Adapter\Exceptions\AdapterRuntimeError
{last PHP error message}
Error message
{last PHP error message} What it means
Stream::gc() globs '{savePath}/{prefix}*' to collect expired session files. glob() errors are suppressed (error_reporting(0) + error_clear_last), and when glob() still returns false the adapter throws AdapterRuntimeError carrying the last PHP error message (or 'Unexpected gc error' if none was recorded). The message is the raw PHP warning, e.g. an open_basedir restriction or permission denied on directory read.
Source
Thrown at phalcon/Session/Adapter/Stream.zep:140
* @return false|int
* @throws AdapterRuntimeError
*/
public function gc(int max_lifetime) -> false | int
{
var file, glob, last, pattern, time;
let pattern = this->path . this->prefix . "*",
time = time() - max_lifetime,
glob = this->getGlobFiles(pattern);
if (false === glob) {
let last = error_get_last();
if (isset(last["message"])) {
let last = last["message"];
} else {
let last = "Unexpected gc error";
}
throw new AdapterRuntimeError(last);
}
if (!empty(glob)) {
for file in glob {
if true === this->phpFileExists(file) &&
true === is_file(file) &&
(filemtime(file) < time) {
this->phpUnlink(file);
}
}
}
return 1;
}
/**
* Ignore the savePath and use local defined path
*/View on GitHub (pinned to b7419de9cd)
Solutions
- Read the exception message: it names the real PHP-level cause (open_basedir restriction, permission denied, No such file or directory)
- Recreate the directory and restore list+write permissions for the PHP user: mkdir -p and chmod/chown as for error 701
- Move savePath to a dedicated directory that tmp cleaners ignore, and register it in tmpfiles.d with an age rule matching session lifetime
- Add a health check that is_dir($savePath) && is_readable($savePath) && is_writable($savePath) before starting the session
Example fix
// before: gc explodes mid-request when the dir vanished
// after: self-heal before session start
$dir = '/var/lib/myapp/sessions';
if (!is_dir($dir)) { @mkdir($dir, 0770, true); }
$session->setAdapter(new Stream(['savePath' => $dir])); Defensive patterns
Strategy: try-catch
Validate before calling
if (!is_dir($savePath) || !is_readable($savePath) || !is_writable($savePath)) {
// recreate or alert before the session (and its gc) runs
@mkdir($savePath, 0770, true);
} Try / catch
try {
$session->start();
} catch (\Phalcon\Session\Adapter\Exceptions\AdapterRuntimeError $e) {
// $e->getMessage() is the raw glob() warning (open_basedir, permission denied, ...)
$logger->error('Session gc failed: ' . $e->getMessage());
// self-heal the directory and reject the request safely
throw new ServiceUnavailableException('Session storage unavailable', 0, $e);
} Prevention
- Exclude the session directory from tmp cleaners (tmpfiles.d rules) or align the cleaning age with session lifetime
- Monitor for disappearance of the savePath directory in long-lived workers
- Treat AdapterRuntimeError messages as the source of truth: they are the suppressed PHP warnings from glob()
When it happens
Trigger: Garbage collection runs on session_start() when session.gc_probability/session.gc_divisor hit, and the save directory has become unreadable since construction: deleted by a tmp cleaner while the worker still runs, permissions changed, open_basedir tightened, the path replaced by a file, or an unmounted network share.
Common situations: systemd-tmpfiles or docker tmp cleaners removing the session directory under long-lived workers; ops tightening open_basedir after the app bootstrapped; NFS/overlay filesystems returning glob errors; directories removed between deploys.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- The session save path cannot be empty
- The session save path [{path}] is not writable
- The session has already been started. To change the id, use
- The session id contains invalid characters
- Cannot set session name after a session has started
AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21).
Data as JSON: /api/errors/fbacecb32207da75.
Report an issue: GitHub.