BookStackApp/BookStack · error · FileUploadException

errors.path_not_writable

Error message

errors.path_not_writable

What it means

FileUploadException with 'errors.path_not_writable' is thrown by FileStorage::uploadFile when the underlying storage disk's writeStream() call fails while saving an uploaded file at $filePath. It wraps any filesystem exception (disk full, permissions, missing directory, S3 errors) into a user-facing localized message naming the target path.

Source

Thrown at app/Uploads/FileStorage.php:67

    public function uploadFile(UploadedFile $file, string $subDirectory, string $suffix, string $extension): string
    {
        $storage = $this->getStorageDisk();
        $basePath = trim($subDirectory, '/') . '/';

        $uploadFileName = Str::random(16) . ($suffix ? "-{$suffix}" : '') . ($extension ? ".{$extension}" : '');
        while ($storage->exists($this->adjustPathForStorageDisk($basePath . $uploadFileName))) {
            $uploadFileName = Str::random(3) . $uploadFileName;
        }

        $fileStream = fopen($file->getRealPath(), 'r');
        $filePath = $basePath . $uploadFileName;

        try {
            $storage->writeStream($this->adjustPathForStorageDisk($filePath), $fileStream);
        } catch (Exception $e) {
            Log::error('Error when attempting file upload:' . $e->getMessage());

            throw new FileUploadException(trans('errors.path_not_writable', ['filePath' => $filePath]));
        }

        return $filePath;
    }

    /**
     * Check whether the configured storage is remote from the host of this app.
     */
    public function isRemote(): bool
    {
        return $this->getStorageDiskName() === 's3';
    }

    /**
     * Get the actual path on system for the given relative file path.
     */
    public function getSystemPath(string $filePath): string
    {

View on GitHub (pinned to 18f8469a1c)

Solutions

  1. Check the Laravel log for the wrapped exception message ('Error when attempting file upload: ...') to find the root cause
  2. Verify the storage directory exists and is writable by the PHP process user (chown/chmod, e.g. chmod -R 775 storage)
  3. Confirm the storage disk config (config/filesystems.php, FILESYSTEM_DISK env) points to a valid, correctly-credentialed disk
  4. Check free disk space and quota on the storage target
  5. For cloud disks, validate credentials/bucket/region and connectivity

Example fix

// before (root cause example: wrong local root)
'root' => storage_path('app/old-uploads'),
// after
'root' => storage_path('app/uploads'),
// then: sudo chown -R www-data:www-data storage/app/uploads && chmod -R 775 storage/app/uploads
Defensive patterns

Strategy: try-catch

Validate before calling

// before upload
$path = storage_path('app/uploads');
if (!is_dir($path) || !is_writable($path)) {
    throw new \RuntimeException("Storage path not writable: $path");
}
if (disk_free_space($path) < $file->getSize()) {
    throw new \RuntimeException('Insufficient disk space');
}

Try / catch

try {
    $stored = $fileStorage->uploadFile($file, $name);
} catch (FileUploadException $e) {
    Log::error('Upload failed: ' . $e->getMessage());
    return back()->withErrors(['file' => 'Storage is not writable; contact the administrator.']);
}

Prevention

When it happens

Trigger: Calling uploadFile() during file uploads when writeStream() throws: directory not writable by PHP/web user, disk quota exceeded, misconfigured storage disk credentials (s3/local path), or invalid/missing stream resource.

Common situations: Web server user lacks write permission on the uploads directory; local storage path wrong after moving servers or changing FILESYSTEM_DISK; S3 keys/bucket misconfigured; disk full on self-hosted instances; SELinux blocking writes.

Related errors


AI-assisted analysis of BookStackApp/BookStack@18f8469a1c (2026-09-02). Data as JSON: /api/errors/b85ae3e7a781c524. Report an issue: GitHub.