BookStackApp/BookStack · error · ImageUploadException

errors.path_not_writable

Error message

errors.path_not_writable

What it means

ImageUploadException with 'errors.path_not_writable' is thrown by ImageService::saveNew when the storage disk's put() fails while writing new image data to $fullPath. Like the file-upload variant, it wraps any disk exception into a localized message that includes the path BookStack attempted to write.

Source

Thrown at app/Uploads/ImageService.php:93

        $fileName = $this->storage->cleanImageFileName($imageName);

        $imagePath = '/uploads/images/' . $type . '/' . date('Y-m') . '/';

        while ($disk->exists($imagePath . $fileName)) {
            $fileName = Str::random(3) . $fileName;
        }

        $fullPath = $imagePath . $fileName;
        if ($secureUploads) {
            $fullPath = $imagePath . Str::random(16) . '-' . $fileName;
        }

        try {
            $disk->put($fullPath, $imageData, true);
        } catch (Exception $e) {
            Log::error('Error when attempting image upload:' . $e->getMessage());

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

        $imageDetails = [
            'name'        => $imageName,
            'path'        => $fullPath,
            'url'         => $this->storage->getPublicUrl($fullPath),
            'type'        => $type,
            'uploaded_to' => $uploadedTo,
        ];

        if (user()->id !== 0) {
            $userId = user()->id;
            $imageDetails['created_by'] = $userId;
            $imageDetails['updated_by'] = $userId;
        }

        $image = (new Image())->forceFill($imageDetails);
        $image->save();

View on GitHub (pinned to 18f8469a1c)

Solutions

  1. Check the Laravel log for 'Error when attempting image upload: ...' for the wrapped cause
  2. Ensure the gallery/uploads directory exists and is writable (chown www-data, chmod 775)
  3. Validate the configured storage disk (FILESYSTEM_DISK and its config) including credentials for cloud disks
  4. Check disk free space/quota

Example fix

// before
sudo chmod 700 /var/www/bookstack/storage/app/uploads/gallery
// after
sudo chown -R www-data:www-data /var/www/bookstack/storage/app/uploads
sudo chmod -R 775 /var/www/bookstack/storage/app/uploads
Defensive patterns

Strategy: try-catch

Validate before calling

$dir = dirname(config('filesystems.disks.' . config('filesystems.default') . '.root', storage_path('app')));
if (is_dir($dir) && !is_writable($dir)) {
    throw new \RuntimeException("Image storage dir not writable: $dir");
}

Try / catch

try {
    $image = $imageService->saveNewFromUpload($file, 'gallery', $pageId);
} catch (ImageUploadException $e) {
    Log::error('Image save failed: ' . $e->getMessage());
    return back()->withErrors(['image' => 'Could not save image; storage target is not writable.']);
}

Prevention

When it happens

Trigger: Calling saveNew() directly or via saveNewFromUpload()/saveNewFromBase64Uri() when $disk->put() throws: unwritable gallery directory, disk quota full, misconfigured storage disk (local root missing, s3 credentials invalid).

Common situations: www-data cannot write to uploads/gallery path; FILESYSTEM_DISK switched to s3 with bad credentials; moved instance without copying/migrating storage permissions; full disk on self-hosted servers.

Related errors


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