Python Create Directory if Not Exist
Learn how to **python create directory if not exist** using `pathlib` and `os.makedirs`. Avoid common pitfalls like race conditions with our expert guide &
By Suraj Ahmed
10th Jun 2026
Last updated: 10th Jun 2026

Your script is trying to save a file, the path looks right, and then Python throws FileNotFoundError. In most cases, the file isn't the actual problem. The parent directory doesn't exist.
That sounds trivial until you hit it in a backend worker, a test runner, or an upload flow where multiple processes can reach the same path at once. Then "just create the folder first" turns into a small reliability problem. The usual beginner pattern, checking with os.path.exists() and then creating the directory, often isn't the best production choice.
If you're looking for the shortest correct answer to Python create directory if not exist, use Path(...).mkdir(parents=True, exist_ok=True). That's the modern, readable pattern. After that, the main challenge is knowing when path checks are misleading, why race conditions happen, and why permission errors often show up right after you create the directory.
That matters whether you're writing a one-off script or wiring file generation into a mobile product workflow like config export, image uploads, build artifacts, or local test fixtures. If your team also deals with broader file handling concerns in app workflows, this overview of mobile file management patterns is a useful companion.
Introduction
The common failure looks like this. You try to write logs/app/output.txt, and Python complains because logs/app isn't there yet. A lot of examples online solve that with a check like if not os.path.exists(...), but that leaves out the details that matter once code runs outside your laptop.
The safer answer is to create directories in a way that handles nested paths and doesn't crash if another part of the app creates the same folder first. You also need to know the difference between "the path exists" and "the path is a directory," because those are not the same thing.
For most modern projects, the best default is simple:
Path("logs/app").mkdir(parents=True, exist_ok=True)
That one line is enough in many cases. The rest of the article is about why it works, where older os-based code still matters, and what to do when the actual problem turns out to be permissions instead of creation.
The Modern and Recommended Method Using Pathlib
Use pathlib as the default in new Python code:
from pathlib import Path
Path("uploads/user_123/avatars").mkdir(parents=True, exist_ok=True)

It solves the two problems that show up in real applications. Nested folders may not exist yet, and repeated startup or concurrent workers should not fail just because the directory is already there.
Why this is the right default
Path(...).mkdir(parents=True, exist_ok=True) is the pathlib version of mkdir -p.
Each argument has a job:
Path("uploads/user_123/avatars")creates a path object, which is easier to work with than passing raw strings around a growing codebase.mkdir()creates the target directory.parents=Truecreates missing intermediate directories.exist_ok=Trueavoids raising an error if the directory already exists.
That last flag matters more than many beginner examples admit. In production, code often runs more than once. A web app may start multiple workers. A background job may retry. A deployment script may be rerun. If the directory is already present, that is usually a normal state, not an error.
Why pathlib tends to age better
The biggest advantage is readability once path handling spreads beyond one line.
user_dir = Path("uploads") / "user_123" / "avatars"
user_dir.mkdir(parents=True, exist_ok=True)
That style keeps path construction and file operations consistent:
avatar_file = user_dir / "profile.png"
You do not need to remember whether a helper expects strings, slashes, or os.path.join(). The code reads like path manipulation, because that is what it is.
A short visual walkthrough can help if you want to see the pattern in action:
One practical gotcha
exist_ok=True handles the case where the directory is already there. It does not fix a bad path.
If uploads/user_123 already exists as a file, not a directory, mkdir() still fails. That is correct behavior. The path exists, but it is the wrong kind of thing. This is one reason I prefer the mkdir call itself over a separate "does it exist?" check. The create operation tells you whether the filesystem state is usable.
For modern code, pathlib is usually the clearest choice. It keeps directory creation short, safe for common reruns, and easier to maintain once the codebase grows.
The Classic Approach with the OS Module
Older scripts and long-lived codebases still use os, and that is fine. If the rest of the module already works with string paths, environment variables, and os.path, the cleanest choice is often to stay consistent:
import os
os.makedirs("uploads/user_123/avatars", exist_ok=True)
That gives you the same practical behavior as the pathlib version. It creates the full directory tree and treats an existing directory as a normal state.
os.mkdir and os.makedirs solve different problems
A common mistake is to confuse os.mkdir and os.makedirs:
os.mkdir(path)creates a single directory.os.makedirs(path, exist_ok=True)creates the full parent chain if needed.
That difference matters the first time you switch from cache to cache/images/tmp. os.mkdir("cache") works if the parent already exists. os.mkdir("cache/images/tmp") fails if cache/images is missing.
For nested paths, use os.makedirs(path, exist_ok=True). That is the os equivalent of Path(path).mkdir(parents=True, exist_ok=True), and it is the pattern covered in Line Serve's guide to Python directory creation.
A quick comparison helps:
| Method | Good for | Problem |
|---|---|---|
os.mkdir("cache") | Single folder in an existing parent | Fails on nested paths |
os.makedirs("cache/images/tmp", exist_ok=True) | Nested directory trees | Less readable if the rest of the code uses pathlib |
Path("cache/images/tmp").mkdir(parents=True, exist_ok=True) | Modern Python projects | Best when path handling already uses Path objects |
Why the old pre-check pattern causes trouble
A lot of older Python examples still do this:
if not os.path.exists(path):
os.makedirs(path)
It reads clearly, but it splits one filesystem action into two. That creates a gap where another process can change the state before your code finishes.
The safer os pattern is to ask for the end state directly:
os.makedirs(path, exist_ok=True)
That approach also gives better signal in real deployments. If the call still fails, the problem is often not "directory already exists." It is usually a permission issue, a read-only mount, or a file sitting where a directory should be.
When keeping os is the better choice
I keep the os version in a few specific cases. Legacy modules are one. Rewriting everything to pathlib can create noisy diffs without improving behavior. Small utility scripts are another, especially if they already pass string paths through shell commands or older library APIs.
Interop matters too.
If the surrounding code already uses os.path.join, os.environ, and low-level process calls, os.makedirs(..., exist_ok=True) fits naturally and stays easy to follow.
Handling Race Conditions for Production-Ready Code
The most common unsafe pattern is this:
if not os.path.exists(path):
os.makedirs(path)
It looks harmless. It isn't.
If two requests hit the same upload path at nearly the same time, both can see "directory missing" and both can try to create it. One succeeds. The other crashes with a "already exists" style failure. That's a race condition.

Prefer direct creation over pre-checks
The simplest reliable fix is to skip the separate existence check and create the directory with exist_ok=True.
Examples:
Path(path).mkdir(parents=True, exist_ok=True)
or
os.makedirs(path, exist_ok=True)
That pattern is usually enough for web apps, job runners, and test suites because the creation call itself gracefully handles the case where another process got there first.
When try and except still help
Sometimes you need a manual pattern. Maybe you're supporting code paths without exist_ok, or maybe you want tighter error handling around the creation call.
Then use EAFP, "easier to ask forgiveness than permission":
try:
os.makedirs(path)
except FileExistsError:
pass
This is safer than checking first because it treats creation as the operation and handles the collision if it happens.
Operational advice: If multiple workers can touch the same path, avoid "if missing, then create" logic. Ask Python to create it, then handle the specific failure you expect.
The workflow people actually need
A lot of directory questions are really file-writing questions. The common need isn't "make a folder." It's "save this file, and make sure the parent folders exist first."
A Python.org ideas thread highlights exactly that. Developers repeatedly asked for open() to create missing directories automatically, and the practical workaround is to create file.parent first with Path(...).parent.mkdir(parents=True, exist_ok=True) before writing, as discussed in the Python.org thread about creating non-existent directories before open.
That leads to a durable pattern:
from pathlib import Path
file = Path("generated/config/mobile/app.json")
file.parent.mkdir(parents=True, exist_ok=True)
file.write_text("{}")
This is what you want for generated configs, exported reports, log files, uploaded assets, and test snapshots. If your scripts also need clean shutdown behavior around file generation, this guide on ending a Python script safely pairs well with the filesystem side of the problem.
A quick note on mode
Both os.makedirs and Path.mkdir accept a mode argument. That's useful when you want a directory created with a specific permission intent from the start, especially on shared servers.
For example:
Path("/var/www/shared-cache").mkdir(mode=0o775, parents=True, exist_ok=True)
The exact result still depends on your environment and process settings, but the important point is that directory creation isn't just about existence. In deployment, access rules often matter more than the create call itself.
A Practical Guide to Directory Permissions
A directory can exist and still be unusable. That's the frustrating part.
You create /var/www/cache, your code runs without a creation error, and then the next write fails because the application user can't write into it. People often misread that as a path bug when it's really a permissions problem.
Using mode deliberately
Both major APIs support a mode parameter. In pathlib, that looks like this:
Path("/var/www/cache").mkdir(mode=0o775, parents=True, exist_ok=True)
The octal value describes the permission intent for the created directory. In practice, teams often use this when a web server user needs write access while other users should have more limited access.
What to check when writes still fail
A short troubleshooting list saves time:
- Wrong owner: The app process may not own the directory it just inherited or mounted.
- Wrong group: Shared write access may depend on the right group membership.
- Wrong path type: The path may exist, but it may not be a directory.
- Environment overrides: Container volumes, deployment users, and host settings can affect the final permissions.
If you're working through broader app-level access issues, this overview of how collaborative app builders handle permissions gives useful system context.
A successful
mkdironly tells you the folder was created. It doesn't guarantee your app can use it the way you expect.
Keep the permission check close to the write path
The most practical habit is to validate directory creation where the file write happens, not in some distant setup script. That keeps the problem local. When uploads, exports, or generated assets fail, you want one code path that can verify the target directory, create it if needed, and surface a meaningful error if permissions block the write.
Common Pitfalls and How to Avoid Them
Most bugs in this area come from using the wrong function or checking the wrong condition.

Do this, not that
| Do this | Not that |
|---|---|
Use Path(path).mkdir(parents=True, exist_ok=True) for nested directories | Use os.mkdir() for a multi-level path |
Use Path.is_dir() or os.path.isdir() when you need a directory | Use os.path.exists() and assume it means "folder" |
| Create the parent of a file path before writing the file | Assume open(..., "w") will create missing directories |
| Handle creation directly | Split it into a check and create sequence unless you have a specific reason |
exists() is broader than most people think
This is a subtle but important trap. os.path.exists() returns True for files and directories. If your code only needs a directory, that check is too broad.
A practical method is to resolve the target path, check whether it's already a directory with os.path.isdir() or Path.is_dir(), then create it recursively only if needed. That avoids treating an existing file as a valid directory, which is exactly why GeeksforGeeks distinguishes isdir() from exists() for this use case.
Here is the safer shape:
from pathlib import Path
p = Path("output/reports")
if p.exists() and not p.is_dir():
raise RuntimeError(f"{p} exists but is not a directory")
p.mkdir(parents=True, exist_ok=True)
The checks that actually help
If you want a compact validation routine, use these questions:
- Is the target supposed to be a directory or a file path?
- If it exists, is it the right type?
- If parents are missing, should they be created automatically?
- Could more than one process hit this path?
- Will the current user be able to write after creation?
A production-minded snippet
This is close to what I'd use for real app code:
from pathlib import Path
def ensure_dir(path: str | Path) -> Path:
p = Path(path)
if p.exists() and not p.is_dir():
raise RuntimeError(f"Path exists and is not a directory: {p}")
p.mkdir(parents=True, exist_ok=True)
return p
It handles the path-type problem, creates nested directories, and doesn't rely on a race-prone existence check before creation.
The biggest mistake isn't forgetting to create the directory. It's assuming "exists" means "ready to use."
Conclusion Your Go-To Strategy
For modern Python, the default answer is straightforward. Use Path(...).mkdir(parents=True, exist_ok=True).
It's readable, handles nested directories, and maps cleanly to how real applications work. If you're maintaining older code, os.makedirs(path, exist_ok=True) is the direct counterpart and still perfectly respectable.
When code runs in concurrent environments, avoid the naive check-then-create habit. Either rely on exist_ok=True or wrap creation in try and except FileExistsError when you need explicit handling. And if a directory gets created but writes still fail, stop staring at the path string and check permissions next.
These few patterns cover most real filesystem code you'll write around uploads, logs, generated files, config output, and test artifacts. Once you use them consistently, you prevent a whole category of noisy runtime failures.
If you're building mobile products and want to move from rough idea to working interface quickly, RapidNative is worth a look. It helps founders, PMs, designers, and developers turn prompts, sketches, and PRDs into shareable React Native apps with real code, which makes it easier to prototype flows, validate features, and hand clean output to engineering.
Ready to build your app?
Turn your idea into a production-ready React Native app in minutes.
Free tools to get you started
Free AI PRD Generator
Generate a professional product requirements document in seconds. Describe your product idea and get a complete, structured PRD instantly.
Try it freeFree AI App Name Generator
Generate unique, brandable app name ideas with AI. Get creative name suggestions with taglines, brand colors, and monogram previews.
Try it freeFree AI App Icon Generator
Generate beautiful, professional app icons with AI. Describe your app and get multiple icon variations in different styles, ready for App Store and Google Play.
Try it freeFrequently asked questions
What is RapidNative?
RapidNative is an AI-powered mobile app builder. Describe the app you want in plain English and RapidNative generates real, production-ready React Native screens you can preview, edit, and publish to the App Store or Google Play.
Can I export the code?
Yes. RapidNative generates clean React Native and Expo code that you can export at any time. No lock-in, no proprietary format. Hand it to your developers or keep building inside RapidNative.
Is RapidNative free to use?
Yes. You can build apps on the free plan with no credit card required. Paid plans unlock unlimited AI generations, code export, and direct publishing to the App Store and Google Play.
Do I need to know how to code?
No. Most users build apps by describing what they want in plain English. Developers can drop into the code whenever they want more control, but coding is optional.
How long does it take to build an app?
Most users have a working first screen in under a minute. A full MVP usually takes a few hours instead of the weeks or months traditional development requires.