{"record":{"id":"87a780ca2c4357db","repo":"Intervention/image","slug":"can-t-write-to-path-directory-dir-is-n","errorCode":null,"errorMessage":"Can't write to path. Directory \"' . $dir . '\" is not writable","messagePattern":"Can't write to path\\. Directory \"' \\. \\$dir \\. '\" is not writable","errorType":"exception","errorClass":"FileNotWritableException","httpStatus":null,"severity":"error","filePath":"src/File.php","lineNumber":93,"sourceCode":"            throw new InvalidArgumentException('Path must not be an empty string');\n        }\n\n        if (strlen($path) > PHP_MAXPATHLEN) {\n            throw new InvalidArgumentException(\n                \"Path is longer than the configured max. value of \" . PHP_MAXPATHLEN,\n            );\n        }\n\n        $dir = pathinfo($path, PATHINFO_DIRNAME);\n\n        if (!is_dir($dir)) {\n            throw new DirectoryNotFoundException(\n                'Can\\'t write to path. Directory \"' . $dir . '\" does not exist',\n            );\n        }\n\n        if (!is_writable($dir)) {\n            throw new FileNotWritableException(\n                'Can\\'t write to path. Directory \"' . $dir . '\" is not writable',\n            );\n        }\n\n        if (is_file($path) && !is_writable($path)) {\n            throw new FileNotWritableException(\n                \"Can't write to path. Existing file \" . $path . \" is not writable\",\n            );\n        }\n\n        // write data\n        $saved = file_put_contents($path, $this->toStream());\n\n        if ($saved === false) {\n            throw new FileNotWritableException(\n                \"Failed to write file to path \" . $path,\n            );\n        }","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/Intervention/image/blob/5598b9e39751c34afc5cdee84abef77f92c26f68/src/File.php#L75-L111","documentation":"After confirming the directory exists, File::save() checks is_writable($dir) and throws FileNotWritableException when the PHP process lacks write permission on that directory. This is a classic ownership mismatch: the directory belongs to a different user than the one PHP runs as (www-data, php-fpm user, queue worker user).","triggerScenarios":"Saving to a directory owned by the deploy user with mode 755 while PHP-FPM runs as www-data; writing into a read-only container mount; SELinux or AppArmor denying writes despite permissive Unix modes; shared hosting where the storage dir belongs to another FTP account.","commonSituations":"Files uploaded/deployed as root and never chowned; Laravel-style storage dirs created by CLI then written by the web user; NFS mounts with root_squash; containers with read-only volumes.","solutions":["Give the PHP user write access: chown the directory to the web server user or a shared group, and use chmod 775","Use ACLs for multi-user setups: setfacl -m u:www-data:rwx storage/uploads","Verify the mount is writable and not read-only (check docker-compose volumes, /etc/fstab, SELinux context)"],"exampleFix":"# before: dir owned by deploy user, mode 755 -> www-data cannot write\n# after:\nsudo chgrp www-data storage/uploads\nsudo chmod 775 storage/uploads\n# or per-user ACL without changing group:\nsudo setfacl -m u:www-data:rwx storage/uploads","handlingStrategy":"validation","validationCode":"$dir = pathinfo($path, PATHINFO_DIRNAME);\nif (!is_writable($dir)) {\n    throw new RuntimeException('Storage directory not writable by PHP user: ' . $dir);\n}\n$encoded->save($path);","typeGuard":null,"tryCatchPattern":"use Intervention\\Image\\Exceptions\\FilesystemException;\n\ntry {\n    $image->save($path);\n} catch (FilesystemException $e) {\n    // check ownership/modes before retrying; do not blind-retry\n    throw new RuntimeException('Cannot save image: ' . $e->getMessage(), 0, $e);\n}","preventionTips":["chown/chgrp storage directories to the PHP-FPM user with mode 775","Use setfacl when multiple users write to the same tree","Verify container mounts are read-write and check SELinux contexts on RHEL-type hosts"],"tags":["php","intervention-image","file","save","permissions","filesystem"],"backgroundTag":"permission-denied","analyzedSha":"5598b9e39751c34afc5cdee84abef77f92c26f68","analyzedAt":"2026-08-23T02:17:31.068Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}