If you need a folder tree such as data/reports/2026 before writing logs or exports, Python’s standard library gives you two solid tools: pathlib.Path.mkdir (usually preferred) and os.makedirs. This page puts pathlib first, matches the official Path.mkdir signature (parents, exist_ok), then covers os.makedirs, comparisons, creating parents before saving a file, errors, and why distutils is obsolete.
Examples use relative paths like data/reports/2026 and, where a full path helps, Path.home() / "reports" so the ideas work on Windows and Linux. Each fenced example is a complete script (imports included) that creates directories under tempfile.TemporaryDirectory() so it does not touch your project tree. Those operations are file system I/O: the opening fences use {run=false} so the in-page Run control is not offered on Cloudflare Pages (Judge0 may block or vary temp filesystem behavior). Copy the snippet into a local python3 shell or script for the same output.
Tested on: Python 3.13.3; kernel 6.14.0-37-generic.
Best way to create directories recursively in Python
Use pathlib.Path with parents=True (create missing parents) and exist_ok=True (do not fail if the leaf directory already exists):
import tempfile
from pathlib import Path
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "logs" / "2026" / "06"
path.mkdir(parents=True, exist_ok=True)
print(path.is_dir())You should see True: the whole chain logs/2026/06 exists under the temp root. That pattern maps directly to project code such as Path("data/reports/2026") when tmp is your working tree.
Create nested directories using pathlib Path.mkdir()
Path.mkdir follows mkdir(mode=0o777, parents=False, exist_ok=False) in the docs. For nested trees you almost always want parents=True. Use mode= only when you care about permission bits on POSIX; on Windows it may be partially ignored.
import tempfile
from pathlib import Path
with tempfile.TemporaryDirectory() as tmp:
nested = Path(tmp) / "data" / "reports" / "2026"
nested.mkdir(parents=True, exist_ok=False)
print(nested)Running it prints an absolute path ending in .../data/reports/2026. A second run with the same path and exist_ok=False would raise FileExistsError unless you set exist_ok=True.
Create directory if it does not exist using pathlib
Set exist_ok=True when rerunning scripts or tests should not crash if the directory is already there. The docs note that exist_ok=True does not help if a file already sits where you want a directory—you still get FileExistsError. See check if a path exists before creating when you expect collisions.
import tempfile
from pathlib import Path
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "cache" / "session"
path.mkdir(parents=True, exist_ok=True)
path.mkdir(parents=True, exist_ok=True)
print("ok", path.is_dir())You should see ok True twice in spirit—the second mkdir is a no-op for an existing directory.
Create nested directories using os.makedirs()
os.makedirs creates the leaf directory and any intermediate ones. With exist_ok=False (the default), an existing target directory raises FileExistsError.
import os
import tempfile
with tempfile.TemporaryDirectory() as tmp:
target = os.path.join(tmp, "logs", "2026", "06")
os.makedirs(target, exist_ok=True)
print(os.path.isdir(target))You should see True. Prefer os.path.join (or pathlib) over hardcoding \ or / so the same code runs cross-platform.
Create directory if it does not exist using os.makedirs
Pass exist_ok=True for idempotent setup, the same idea as Path.mkdir(..., exist_ok=True).
import os
import tempfile
with tempfile.TemporaryDirectory() as tmp:
target = os.path.join(tmp, "out", "render")
os.makedirs(target, exist_ok=True)
os.makedirs(target, exist_ok=True)
print("ready")This prints ready without error. If a file named render already exists, both APIs fail with FileExistsError when you try to create a directory at that path.
pathlib mkdir vs os.makedirs
Path.mkdir(parents=True, exist_ok=True) |
os.makedirs(..., exist_ok=True) |
|
|---|---|---|
| Style | Object-oriented Path objects |
String paths with os |
| Typical use | New application code, composition with read_text / write_text |
Legacy scripts, glue next to other os.* calls |
| Parents | parents=True |
Always recursive for the full path |
| Existence | exist_ok=True |
exist_ok=True (Python 3.2+) |
Pick one style per module for consistency; mixing is fine at boundaries if you convert with Path(p) or os.fspath(p).
Create parent folders before writing a file
Real code often needs the parent of a file path, not the file itself:
import tempfile
from pathlib import Path
with tempfile.TemporaryDirectory() as tmp:
file_path = Path(tmp) / "reports" / "2026" / "june" / "output.txt"
file_path.parent.mkdir(parents=True, exist_ok=True)
file_path.write_text("Report generated")
print(file_path.read_text())You should see Report generated. On user-owned trees you can build under the home directory without hardcoding separators, for example Path.home() / "reports" / "2026" (same mkdir on .parent before writing).
Handle errors while creating directories
Catch PermissionError when the process lacks rights, and OSError for other platform issues (disk full, read-only filesystem, invalid name).
import tempfile
from pathlib import Path
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "logs" / "2026" / "06"
try:
path.mkdir(parents=True, exist_ok=True)
print("created", path.is_dir())
except PermissionError:
print("Permission denied")
except OSError as error:
print(f"Could not create directory: {error}")In normal temp setups this prints created True. For more on failures when opening paths, see FileNotFoundError in Python.
Why distutils.dir_util.mkpath should be avoided
Older tutorials used distutils.dir_util.mkpath(). distutils was removed from the standard library in Python 3.12 (deprecated earlier in 3.10). Do not teach it as a current option; use pathlib.Path.mkdir or os.makedirs instead and migrate any legacy mkpath call sites.
Common mistakes when creating directories
- Omitting
parents=Trueand then wondering why a nested path raisesFileNotFoundErrorbecause an intermediate folder is missing. - Omitting
exist_ok=Truein idempotent jobs so reruns raiseFileExistsErroreven though the tree is already correct. - Importing or copying
distutilsexamples into a Python 3.12+ environment where the module is gone. - Calling
mkdiron a path where a file already exists with the same name—bothpathlibandosrefuse to replace a file with a directory via these calls. - Hardcoding
C:\...or mixing/and\by hand; preferPath(...)oros.path.join. - Opening
Path("reports/june.txt")for write without creatingPath("reports")first—create.parent(or useopenhelpers that document who creates parents). - Swallowing all errors: at minimum distinguish
PermissionErrorfrom otherOSErrortypes so operators can react.
Python create directory quick reference table
| Need | Approach |
|---|---|
| Recursive create (modern) | Path("a/b/c").mkdir(parents=True, exist_ok=True) |
Recursive create (os) |
os.makedirs(os.path.join("a", "b", "c"), exist_ok=True) |
| Parents only for a file | file_path.parent.mkdir(parents=True, exist_ok=True) |
| Default “fail if exists” | omit exist_ok or pass False |
| Sum of permission details on POSIX | set mode= on mkdir where documented |
Summary
For recursive directory creation in current Python, start with pathlib.Path.mkdir(parents=True, exist_ok=True); use os.makedirs(..., exist_ok=True) when string paths and the os module fit your codebase. Create parent directories before writing files with file_path.parent.mkdir(...). Handle PermissionError and OSError, and do not rely on distutils—it is gone from the stdlib in 3.12+. For writing after the tree exists, follow up with write to file patterns in your app.

