{"record":{"id":"54c52e0d0f282a96","repo":"doctrine/orm","slug":"the-directory-s-is-not-writable","errorCode":null,"errorMessage":"The directory \"%s\" is not writable.","messagePattern":"The directory \"(.+?)\" is not writable\\.","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"src/Cache/Region/FileLockRegion.php","lineNumber":55,"sourceCode":"{\n    final public const LOCK_EXTENSION = 'lock';\n\n    /**\n     * @param numeric-string|int $lockLifetime\n     *\n     * @throws InvalidArgumentException\n     */\n    public function __construct(\n        private readonly Region $region,\n        private readonly string $directory,\n        private readonly string|int $lockLifetime,\n    ) {\n        if (! is_dir($directory) && ! @mkdir($directory, 0775, true)) {\n            throw new InvalidArgumentException(sprintf('The directory \"%s\" does not exist and could not be created.', $directory));\n        }\n\n        if (! is_writable($directory)) {\n            throw new InvalidArgumentException(sprintf('The directory \"%s\" is not writable.', $directory));\n        }\n    }\n\n    private function isLocked(CacheKey $key, Lock|null $lock = null): bool\n    {\n        $filename = $this->getLockFileName($key);\n\n        if (! is_file($filename)) {\n            return false;\n        }\n\n        $time    = $this->getLockTime($filename);\n        $content = $this->getLockContent($filename);\n\n        if ($content === false || $time === false) {\n            @unlink($filename);\n\n            return false;","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/doctrine/orm/blob/d9b9ff73016bf598ae07515f97289ce8074e97a5/src/Cache/Region/FileLockRegion.php#L37-L73","documentation":"InvalidArgumentException from FileLockRegion::__construct() (src/Cache/Region/FileLockRegion.php:55). The lock directory exists (or was created), but PHP's is_writable() reports the process cannot write into it — FileLockRegion must create/lock/unlink files there on every READ_WRITE cache access, so it aborts construction instead of failing at first lock.","triggerScenarios":"Directory owned by root or another user with no group/other write bits for the web/CLI user; ACLs denying the PHP user; SELinux/AppArmor confining writes to that path; NFS root-squash making root-owned dirs unwritable.","commonSituations":"Directory created by a root-run deploy script while php-fpm runs as www-data; CLI (root cron) warmed the lock dir with root-owned lock files; container images with root-owned /var directories; moving cache between hosts with preserved ownership.","solutions":["Fix ownership/permissions: chown -R www-data:www-data /var/cache/doctrine-lock && chmod 775 /var/cache/doctrine-lock (and ensure the PHP process user matches).","Have deployment create the directory as the app user (or with correct ownership) instead of root.","Verify with the same user that runs PHP: sudo -u www-data test -w /var/cache/doctrine-lock.","If stale root-owned lock files (*.lock) exist inside, remove them too — lock files, not just the directory, must be writable/unlinkable."],"exampleFix":"# before: deploy script (root) created the dir, php-fpm runs as www-data\n# sudo -u www-data test -w /var/cache/doctrine-lock -> fails\n\n# after\nchown -R www-data:www-data /var/cache/doctrine-lock\nchmod 775 /var/cache/doctrine-lock","handlingStrategy":"validation","validationCode":"if (! is_writable($lockRegionDirectory)) {\n    throw new RuntimeException(\n        sprintf('Lock dir %s not writable by %s', $lockRegionDirectory, get_current_user())\n    );\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Create cache/lock directories as the same user that runs PHP (php-fpm user), not root.","Include permission setup in container images (mkdir/chown) rather than volume defaults.","After CLI warmup runs as root, chown lock files back to the web user before serving traffic."],"tags":["doctrine-orm","second-level-cache","file-locks","permissions","filesystem"],"backgroundTag":"directory-not-writable","analyzedSha":"d9b9ff73016bf598ae07515f97289ce8074e97a5","analyzedAt":"2026-08-21T06:13:15.863Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}