getgrav/grav · error · RuntimeException
Creating directory failed for {filepath}
Error message
Creating directory failed for {filepath} What it means
AbstractFile::lock() needs a write handle, so before @fopen it guarantees the file's parent directory exists via mkdir(); when that returns false it throws this RuntimeException with the file path embedded. It is an environment failure, not a data problem: the PHP process cannot create the directory — permissions, open_basedir, a read-only filesystem, or a path component that is a regular file.
Source
Thrown at system/src/Grav/Framework/File/AbstractFile.php:191
/**
* {@inheritdoc}
* @see FileInterface::getModificationTime()
*/
public function getModificationTime(): int
{
return is_file($this->filepath) ? (int)filemtime($this->filepath) : time();
}
/**
* {@inheritdoc}
* @see FileInterface::lock()
*/
public function lock(bool $block = true): bool
{
if (!$this->handle) {
if (!$this->mkdir($this->getPath())) {
throw new RuntimeException('Creating directory failed for ' . $this->filepath);
}
$this->handle = @fopen($this->filepath, 'cb+') ?: null;
if (!$this->handle) {
$error = error_get_last();
$message = $error['message'] ?? 'Unknown error';
throw new RuntimeException("Opening file for writing failed on error {$message}");
}
}
$lock = $block ? LOCK_EX : LOCK_EX | LOCK_NB;
// Some filesystems do not support file locks, only fail if another process holds the lock.
$this->locked = flock($this->handle, $lock, $wouldBlock) || !$wouldBlock;
return $this->locked;
}
View on GitHub (pinned to 6040efed04)
Solutions
- Fix ownership and permissions on every parent directory so the PHP user can write: chown -R www-data:www-data <dir> && chmod -R 775 <dir>.
- Create the directory manually (mkdir -p) and verify the PHP user can write a test file into it.
- Check open_basedir (php -i / admin panel) covers the path and the mount is not read-only.
- Standardize on a single PHP user for CLI and web so directory ownership never flips.
Example fix
# before: RuntimeException "Creating directory failed for /var/www/grav/user/data/plugin/x.json" ls -ld /var/www/grav/user/data # owned by root:root sudo chown -R www-data:www-data /var/www/grav/user/data sudo chmod -R 775 /var/www/grav/user/data # after: lock() creates the directory and opens the handle
Defensive patterns
Strategy: validation
Validate before calling
$dir = dirname($filepath);
if (!is_dir($dir) && !is_writable(dirname($dir))) {
// mkdir inside lock() will fail: fix permissions or choose another path
} Try / catch
try {
$file->lock();
} catch (\RuntimeException $e) {
// filesystem/environment problem: surface to admin, do not retry blindly
$alerts->notify('Cannot lock ' . $file->getFilePath() . ': ' . $e->getMessage());
} Prevention
- Provision storage directories (with correct ownership) as part of deployment, not lazily at runtime.
- Run web and CLI PHP under the same user to keep directory ownership consistent.
- Monitor free space and mount states; read-only mounts turn into this error immediately.
When it happens
Trigger: Calling lock() on a File whose directory does not exist and cannot be created: parent directory not writable by the web-server/PHP-FPM user; path outside open_basedir; read-only mount; a parent path segment exists as a file rather than a directory.
Common situations: Fresh deploy where storage directories were created by root during upload and never chowned; shared hosting with open_basedir restrictions; containers with read-only volumes; SELinux/AppArmor denials after a server migration.
Related errors
- Unable to create directory: %s
- Opening file for writing failed on error {$message}
- Unable to %s: %s
- Failed to save file {filepath}
- Bad Data
AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17).
Data as JSON: /api/errors/c09ebb007f28066e.
Report an issue: GitHub.