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
- Check the Laravel log for the wrapped exception message ('Error when attempting file upload: ...') to find the root cause
- Verify the storage directory exists and is writable by the PHP process user (chown/chmod, e.g. chmod -R 775 storage)
- Confirm the storage disk config (config/filesystems.php, FILESYSTEM_DISK env) points to a valid, correctly-credentialed disk
- Check free disk space and quota on the storage target
- 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
- Provision storage directories with correct ownership/permissions in deployment scripts (chown www-data, chmod 775)
- Monitor disk free space and quotas with alerting
- Test the configured storage disk with a smoke-test write during health checks
- Keep FILESYSTEM_DISK and disk credentials in validated env config
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
- errors.path_not_writable
- Failed to load key from file path with error: {$exception->g
- auth.email_confirm_send_error
- auth.registration_email_domain_invalid
- errors.chapter_not_found
AI-assisted analysis of BookStackApp/BookStack@18f8469a1c (2026-09-02).
Data as JSON: /api/errors/b85ae3e7a781c524.
Report an issue: GitHub.