The safest way to check if a file exists and create it if not in Python is one call: open it in "x" mode inside a try block. Python creates a missing file and raises FileExistsError for an existing one, so old data stays safe:
try:
with open("report.txt", "x", encoding="utf-8") as f:
f.write("first line\n")
except FileExistsError:
pass # the file was already there
Every run you see below was done on Python 3.12.5 on Windows 11, and the output blocks are copied from those runs.
In this tutorial, I’ll show the classic exists-then-create check, the x mode, Path.touch(), append mode for logs, and how to create missing folders along the way.
Check if a file exists with os.path, then create it
The most readable version asks first and creates second. I ran the same check twice so you can see both branches:
import os
from pathlib import Path
Path("customers.txt").unlink(missing_ok=True) # start clean for the demo
for run in (1, 2):
if not os.path.exists("customers.txt"):
with open("customers.txt", "w", encoding="utf-8") as f:
f.write("Emily Carter,Austin TX\n")
print(f"run {run}: customers.txt was missing, created it")
else:
print(f"run {run}: customers.txt already exists, left it alone")
print("isfile:", os.path.isfile("customers.txt"), "| exists:", os.path.exists("customers.txt"))
Path("archive").mkdir(exist_ok=True)
print("folder 'archive' -> exists:", os.path.exists("archive"), "| isfile:", os.path.isfile("archive"))
Output:
run 1: customers.txt was missing, created it
run 2: customers.txt already exists, left it alone
isfile: True | exists: True
folder 'archive' -> exists: True | isfile: False

customers.txt; the second finds it and leaves it alone. A folder passes exists() but fails isfile().Notice the last line. os.path.exists() is True for folders too, so os.path.isfile() is the safer test when you really mean a file.
Every way to test for a file, including Path.is_file(), is in checking if a file exists in Python.
This pattern has one weak spot. Between the check and the open() call, another process could create the same file, and "w" would then wipe it. That gap is called a race condition.
Create the file only if it doesn’t exist with x mode
Mode "x" does the check and the create in one step, so there’s no gap. Here I try to create the same invoice twice:
from pathlib import Path
Path("invoice_1001.txt").unlink(missing_ok=True) # start clean for the demo
def create_once(name, text):
try:
with open(name, "x", encoding="utf-8") as f: # x = create, fail if it exists
f.write(text)
return "created"
except FileExistsError:
return "skipped, already there"
print(create_once("invoice_1001.txt", "Invoice 1001 for $249.99, due 2026-11-15\n"))
print(create_once("invoice_1001.txt", "Invoice 1001 for $0.00\n"))
print("file holds:", Path("invoice_1001.txt").read_text(encoding="utf-8").strip())
Output:
created
skipped, already there
file holds: Invoice 1001 for $249.99, due 2026-11-15

FileExistsError, so the original invoice text survives.The second call didn’t overwrite the $249.99 invoice with $0.00. That’s exactly what you want for receipts, exports and anything else you can’t afford to lose.
I reach for x whenever a script might run twice. The other ways to make files are in creating a new file in Python, and every mode is explained in opening a file in Python.
Create an empty file with pathlib touch()
Path.touch() works like the Linux touch command. With exist_ok=True it creates a missing file and quietly accepts an existing one:
from pathlib import Path
notes = Path("notes.txt")
notes.unlink(missing_ok=True) # start clean for the demo
notes.touch(exist_ok=True) # missing -> creates an empty file
print("after 1st touch:", notes.exists(), notes.stat().st_size, "bytes")
notes.write_text("Call Denver office on 2026-11-02\n", encoding="utf-8")
notes.touch(exist_ok=True) # exists -> content stays
print("after 2nd touch:", notes.stat().st_size, "bytes ->", notes.read_text(encoding="utf-8").strip())
try:
notes.touch(exist_ok=False)
except FileExistsError as err:
print("exist_ok=False ->", type(err).__name__)
Output:
after 1st touch: True 0 bytes
after 2nd touch: 34 bytes -> Call Denver office on 2026-11-02
exist_ok=False -> FileExistsError

touch() makes an empty file, the second keeps the saved note, and exist_ok=False raises FileExistsError.Touching an existing file doesn’t clear it. It only updates the modified time, which is why the note about the Denver office is still there afterwards.
Use touch() when you just need the file to exist, such as a marker or lock file. When you also need to write a first line, x mode is cleaner.
Create or append to a file with a mode
For logs you usually want “create it the first time, add to it after that”. Mode "a" does both, and checking first tells you when to write a header:
from pathlib import Path
log = Path("sales_log.csv")
log.unlink(missing_ok=True) # start clean for the demo
def add_sale(date, city, amount):
new_file = not log.exists()
with open(log, "a", encoding="utf-8") as f: # a = create if missing, else add to the end
if new_file:
f.write("date,city,amount\n")
f.write(f"{date},{city},{amount:.2f}\n")
add_sale("2026-10-26", "Austin", 49.99)
add_sale("2026-10-27", "Seattle", 120.50)
print(log.read_text(encoding="utf-8").strip())
Output:
date,city,amount
2026-10-26,Austin,49.99
2026-10-27,Seattle,120.50
The header appears once even though add_sale() ran twice. Mode "a" never deletes anything, so you can call it from a scheduled job every day.
Mode "w" is the opposite: it empties the file before writing. When that’s what you want, see overwriting a file in Python.
Create the missing folders before the file
None of these methods create folders. If the parent folder isn’t there, open() raises FileNotFoundError, so make the folders first:
from pathlib import Path
import shutil
shutil.rmtree("reports", ignore_errors=True) # start clean for the demo
target = Path("reports") / "2026" / "october.txt"
try:
with open(target, "x", encoding="utf-8") as f:
f.write("October total: $18,420.00\n")
except FileNotFoundError as err:
print("no folder yet ->", type(err).__name__)
target.parent.mkdir(parents=True, exist_ok=True) # make reports\2026 first
with open(target, "x", encoding="utf-8") as f:
f.write("October total: $18,420.00\n")
print("created:", target, "->", target.exists())
Output:
no folder yet -> FileNotFoundError
created: reports\2026\october.txt -> True

reports\2026 is missing; after mkdir(parents=True, exist_ok=True) the file is created.parents=True builds the whole chain, and exist_ok=True stops an error when the folders already exist. More options are in creating a directory in Python.
If the script must find files next to itself, build the path from __file__, as shown in getting the path of the current file in Python.
Which method should you use to create a file if it’s missing?
All five work. Pick the one that matches what should happen when the file is already there:
| Method | If the file exists | Best for |
|---|---|---|
os.path.exists() + "w" | Skipped by your if | Simple scripts, one process |
open(path, "x") | Raises FileExistsError | Never overwriting data |
Path.touch(exist_ok=True) | Kept, time updated | Empty marker files |
open(path, "a") | New text goes at the end | Logs and CSV rows |
mkdir(parents=True) first | – | Paths with new folders |
My default is x mode for new files and a mode for logs. When you also need to report a missing file to the user, see handling a file that does not exist in Python.
These guides pick up once your file exists:
- Write a string to a file in Python
- Read a file line by line in Python
- Python with open as file
- Clear a file in Python
Frequently asked questions
How do I create a file in Python only if it doesn’t exist?
Open it with open(path, "x") inside try and catch FileExistsError. The file is created only when it’s missing.
Does open() with w mode create a file if it doesn’t exist?
Yes, but it also empties the file if it already exists. Use "a" or "x" when the old content must stay.
Does Path.touch() overwrite an existing file?
No. With exist_ok=True it keeps the content and only updates the modified time.
Should I use os.path.exists() or os.path.isfile()?
Use isfile() when you need a regular file; exists() is also True for folders.