ErrLookup › Background articles › mkdir permission denied (EACCES): failed to create directory errors explained
mkdir permission denied (EACCES): failed to create directory errors explained
"mkdir: permission denied" and messages like "failed to create directory" mean a library called mkdir() or an equivalent (os.MkdirAll, create_dir_all, FileHelper::createDirectory, mkdirs) and the operating system refused to create the directory. You meet this error at startup or first write: a tool or service tries to create its state, cache, runtime, or output directory and hits a filesystem-level refusal — permissions, a regular file occupying the path, a read-only mount, or a full disk.
Distilled from 111 documented records across 32 repositories.
Background
This family is produced at the OS/filesystem layer, not by library logic. Every record traces back to a single syscall family: PHP's mkdir(), Go's os.MkdirAll, Rust's std::fs::create_dir_all, Java's File.mkdirs, or Node's mkdirSync. When the kernel returns EACCES, ENOTDIR, EROFS, ENOSPC, or a similar errno, the library wraps it with its own message naming the path — "Failed to create directory \"{$path}\"" in Yii, "failed to create tmp directory: %w" in PentAGI, "create main directory: %w" in Pulumi — and aborts. The wrapped errno inside the message is usually the real diagnosis; the outer text only tells you which directory the library wanted.
Libraries differ mainly in when they call mkdir and how fatal the failure is. Some do it once at startup (Fluentd's root_dir, containerd's volatile container root, caveman's runtime and socket directories), so the whole process refuses to boot. Others do it per-operation: Pulumi's SDK generation, crush's MultiEdit parent-directory creation, waveterm's dated backup directories, or Hadoop's lazy-persist eviction, where each write or copy fails until the directory exists. Recursive helpers (MkdirAll, create_dir_all, mkdir_p) create missing parents as they go, while non-recursive calls (PHP's default mkdir in Leantime and October CMS, Gradle's final mkdirs fallback) fail if an intermediate component is missing. A recurring subtlety: several implementations (Yii's is_dir re-check after issue #9288, containerd's snapshot rollback, Gradle's isDirectory follow-up) re-check whether the directory appeared anyway, to tolerate races where a concurrent process created it between the failed mkdir and the check.
From the caller's side the failure usually looks identical across languages: a wrapped *fs.PathError in Go, an io::Error in Rust, an Errno exception in Ruby, or an ErrorException in PHP (which only reaches a catch block if an error handler converts mkdir's E_WARNING, as Yii's does). Because the underlying cause is environmental, the fix is almost never in code: it is chown/chmod on the parent, removing a regular file that shadows the intended directory, remounting a read-only volume read-write, freeing disk space, or adjusting SELinux/AppArmor or open_basedir policy.
Common causes
- Wrong ownership or missing write permission. The process user cannot write to the nearest existing ancestor of the target path. Classic patterns: the app runs as www-data, fluent, or hdfs but the parent is root-owned, or a one-off sudo run created ~/.config or ~/.beads as root so later non-root runs get EACCES (beads, caveman).
- A regular file occupies the directory path. A file exists where a directory is needed — .beads, runtime, run, lazy-persist, sdks, or waveai-backups — so mkdir gets ENOTDIR/EEXIST. Often left behind by an interrupted run or a botched restore.
- Read-only filesystem or mount. The target sits on a read-only container layer, a :ro volume mount, a read-only root filesystem (readOnlyRootFilesystem in Kubernetes), or a CI artifact checkout. mkdir is refused with EROFS regardless of permissions.
- Disk full or inode exhaustion. ENOSPC on the volume holding the target — snapshot roots, data dirs, caches, or RAM_DISK eviction targets in Hadoop. Inode exhaustion produces the same failure even with free bytes.
- Missing parent directories with non-recursive mkdir. Non-recursive calls (PHP mkdir without the recursive flag, Gradle's last-resort mkdirs, a misconfigured plugin directory) cannot create intermediate components. Leantime and October CMS both surface this when parent trees like app/Plugins do not exist; passing recursive=true fixes it.
- Security policy: SELinux, AppArmor, ACLs, open_basedir. Mandatory access controls or PHP's open_basedir deny creation even when Unix permissions look correct. Check the node audit log (ausearch -m avc) or restorecon the directory; in PHP, widen open_basedir.
- HOME unset or pointing somewhere unwritable. Tools that build paths from $HOME (beads, caveman) fail when HOME is unset — resolving to an unwritable path like / — or when XDG_CONFIG_HOME points at a foreign-owned directory.
- Boot-order and race conditions. The process starts before its mount exists (containerd's /run ordering) or the directory is deleted concurrently (Pulumi's parent vanishing between calls, beads' raced deletion). Implementations like Yii and Gradle re-check with is_dir/isDirectory to tolerate a concurrent creator.
What usually fixes it
- [object Object]
- [object Object]
- [object Object]
- [object Object]
- [object Object]
- [object Object]
Documented occurrences
- Failed to create directory "{$path}": {message} (yiisoft/yii2)
- newExternalProxiedServerUOWProvider: mkdir %s: %w (gastownhall/beads)
- Unable to create test kit directory: {} (gradle/gradle)
- LazyWriter fail to find or create lazy persist dir: ${lazyPersistDir} (apache/hadoop)
- mkdir_no_fail({:?}): {} (facebook/flow)
- failed to create volatile container root directory %q: %w (containerd/containerd)
- failed to create app resource dir (GraphiteEditor/Graphite)
- failed to create snapshot dir %s: %w (containerd/containerd)
- failed to create deletion queue directory %s: %w (cilium/cilium)
- create main directory: %w (pulumi/pulumi)
- failed to create git config directory: %w (gastownhall/beads)
- failed to create tmp directory: %w (vxcontrol/pentagi)
- failed to create root directory:#{root_dir}, #{e.inspect} (fluent/fluentd)
- native session key mkdir: %w (JuliusBrussee/caveman)
- create output directory: %w (cilium/cilium)
- failed to create parent directories: %w (charmbracelet/crush)
- cannot create shared server directory %s: %w (gastownhall/beads)
- Directory "%s" was not created (Leantime/leantime)
- Error building local installation folder (${join(baseFolder, ...pathSegments)}): ${e} (hcengineering/platform)
- failed to create .beads directory: %w (gastownhall/beads)
…and 91 more across the corpus — use search.
Honest provenance: generated on 2026-09-02 from AI-assisted analysis of the linked records. See how records are made.