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

  1. 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>.
  2. Create the directory manually (mkdir -p) and verify the PHP user can write a test file into it.
  3. Check open_basedir (php -i / admin panel) covers the path and the mount is not read-only.
  4. 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

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


AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17). Data as JSON: /api/errors/c09ebb007f28066e. Report an issue: GitHub.