{"record":{"id":"8eccf14ca073572b","repo":"BookStackApp/BookStack","slug":"errors-path-not-writable-8eccf1","errorCode":null,"errorMessage":"errors.path_not_writable","messagePattern":"errors\\.path_not_writable","errorType":"exception","errorClass":"ImageUploadException","httpStatus":null,"severity":"error","filePath":"app/Uploads/ImageService.php","lineNumber":93,"sourceCode":"        $fileName = $this->storage->cleanImageFileName($imageName);\n\n        $imagePath = '/uploads/images/' . $type . '/' . date('Y-m') . '/';\n\n        while ($disk->exists($imagePath . $fileName)) {\n            $fileName = Str::random(3) . $fileName;\n        }\n\n        $fullPath = $imagePath . $fileName;\n        if ($secureUploads) {\n            $fullPath = $imagePath . Str::random(16) . '-' . $fileName;\n        }\n\n        try {\n            $disk->put($fullPath, $imageData, true);\n        } catch (Exception $e) {\n            Log::error('Error when attempting image upload:' . $e->getMessage());\n\n            throw new ImageUploadException(trans('errors.path_not_writable', ['filePath' => $fullPath]));\n        }\n\n        $imageDetails = [\n            'name'        => $imageName,\n            'path'        => $fullPath,\n            'url'         => $this->storage->getPublicUrl($fullPath),\n            'type'        => $type,\n            'uploaded_to' => $uploadedTo,\n        ];\n\n        if (user()->id !== 0) {\n            $userId = user()->id;\n            $imageDetails['created_by'] = $userId;\n            $imageDetails['updated_by'] = $userId;\n        }\n\n        $image = (new Image())->forceFill($imageDetails);\n        $image->save();","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/BookStackApp/BookStack/blob/18f8469a1c72f8cc8497e9372635e6dea5028071/app/Uploads/ImageService.php#L75-L111","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Check the Laravel log for 'Error when attempting image upload: ...' for the wrapped cause","Ensure the gallery/uploads directory exists and is writable (chown www-data, chmod 775)","Validate the configured storage disk (FILESYSTEM_DISK and its config) including credentials for cloud disks","Check disk free space/quota"],"exampleFix":"// before\nsudo chmod 700 /var/www/bookstack/storage/app/uploads/gallery\n// after\nsudo chown -R www-data:www-data /var/www/bookstack/storage/app/uploads\nsudo chmod -R 775 /var/www/bookstack/storage/app/uploads","handlingStrategy":"try-catch","validationCode":"$dir = dirname(config('filesystems.disks.' . config('filesystems.default') . '.root', storage_path('app')));\nif (is_dir($dir) && !is_writable($dir)) {\n    throw new \\RuntimeException(\"Image storage dir not writable: $dir\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    $image = $imageService->saveNewFromUpload($file, 'gallery', $pageId);\n} catch (ImageUploadException $e) {\n    Log::error('Image save failed: ' . $e->getMessage());\n    return back()->withErrors(['image' => 'Could not save image; storage target is not writable.']);\n}","preventionTips":["Keep storage ownership/permissions correct after deploys and server moves (deployment script step)","Run a smoke-test write to the disk in health checks","Alert on disk usage thresholds","Validate cloud disk credentials/region on environment setup"],"tags":["php","laravel","image-upload","filesystem","permissions"],"backgroundTag":"path-not-writable","analyzedSha":"18f8469a1c72f8cc8497e9372635e6dea5028071","analyzedAt":"2026-09-02T19:49:33.068Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}