FileExistsError
Something is already at that path. It is the error you want from open(p, 'x') and the one you usually silence with exist_ok=True on mkdir.
FileExistsError(errnoerrno — errno.EEXIST when raised by the OS.type: int · default: null, strerror[, filename])
Raised by
os.mkdir(), Path.mkdir(), open(p, 'x'), os.link(), Windows os.rename()
Message
[Errno 17] File exists: 'path'
Quick fix
os.makedirs(p, exist_ok=True)
Watch out
exist_ok does not help when a FILE has that name
Demo
Live evaluation
Mode 'x' creates a file only if it does not exist yet. draft_1.txt is already there.
Try:
Inputs
nintdraft number
Code
from pathlib import Path Path('draft_1.txt').write_text('v1') n = 2 open(f'draft_{n}.txt', 'x').write('new draft')
Result
9
A successful open(..., 'x').write() returns the number of characters written; on an existing name it refuses instead of truncating — that is the whole point of 'x'. The Handle demo reads e.filename rather than str(e): os.mkdir's message text differs on Windows ([WinError 183]), but the class and filename do not.
Constructor
| Name | Type | Required | Description |
|---|---|---|---|
| errno | int | no | errno.EEXIST when raised by the OS. |
| strerror | str | no | 'File exists' from open(); Windows os.mkdir gives 'Cannot create a file when that file already exists'. |
| filename | str | bytes | None | no | The path that already exists (for os.rename, the source; the target is filename2). |
Attributes
| Attribute | Type | Meaning |
|---|---|---|
| errno | int | None | errno.EEXIST (17 on Linux, macOS and Windows). |
| strerror | str | None | 'File exists' on POSIX and from open(); the Windows API text for os.mkdir and os.rename. |
| filename | str | bytes | None | The path that was in the way. |
| filename2 | str | None | For os.rename / os.link: the destination path. |
Common patterns
Create a folder if needed
exist_ok=True turns "already there" into success — no try/except, no race.
import os os.makedirs('out/logs', exist_ok=True)
Never overwrite: exclusive create
Mode 'x' fails atomically if the file exists, so two processes cannot both think they created it.
try: with open('report.csv', 'x') as f: f.write(data) except FileExistsError: print('report.csv exists, not overwriting')
Lock file
O_CREAT | O_EXCL is the classic cross-process lock: only one caller can create the file.
import os try: fd = os.open('app.lock', os.O_CREAT | os.O_EXCL | os.O_WRONLY) except FileExistsError: raise SystemExit('another instance is running')
Replace a file on every OS
os.rename raises FileExistsError on Windows when the target exists; os.replace overwrites everywhere.
import os os.replace('report.tmp', 'report.csv')
Examples
1. open(..., 'x') on an existing file
from pathlib import Path
Path('report.txt').write_text('v1')
open('report.txt', 'x')
Returns
FileExistsError: [Errno 17] File exists: 'report.txt'2. mkdir twice
import os
os.mkdir('out')
try:
os.mkdir('out')
except FileExistsError as e:
r = e.filename
r
Returns
'out'3. makedirs with exist_ok=True
import os
os.makedirs('out/logs', exist_ok=True)
os.makedirs('out/logs', exist_ok=True)
os.path.isdir('out/logs')
Returns
True4. Path.mkdir(parents=True, exist_ok=True)
from pathlib import Path
Path('a/b/c').mkdir(parents=True, exist_ok=True)
Path('a/b/c').mkdir(parents=True, exist_ok=True)
Path('a/b/c').is_dir()
Returns
True5. exist_ok does not cover a file
from pathlib import Path
Path('out').write_text('oops')
try:
Path('out').mkdir(exist_ok=True)
except OSError as e:
r = type(e).__name__
r
Returns
'FileExistsError'6. errno EEXIST maps to it
import errno
OSError(errno.EEXIST, 'File exists', 'a.txt')
Returns
FileExistsError(17, 'File exists')7. os.replace overwrites everywhere
import os
from pathlib import Path
Path('new.txt').write_text('v2')
Path('old.txt').write_text('v1')
os.replace('new.txt', 'old.txt')
(Path('old.txt').read_text(), os.path.exists('new.txt'))
Returns
('v2', False)Pitfalls
1. 'w' silently overwrites
open(p, 'w') truncates an existing file without a word. When losing the old content would be a bug, use 'x' and handle FileExistsError.
mode 'w'
from pathlib import Path Path('notes.txt').write_text('important') open('notes.txt', 'w').write('draft') Path('notes.txt').read_text()
'draft'
mode 'x'
from pathlib import Path Path('notes.txt').write_text('important') try: open('notes.txt', 'x').write('draft') except FileExistsError: pass Path('notes.txt').read_text()
'important'
2. Swallowing FileExistsError instead of exist_ok
except FileExistsError: pass assumes "the folder is already there". But a FILE with that name raises the same error — the folder you rely on does not exist. exist_ok=True only forgives an existing directory, so the file case still surfaces.
except: pass
import os from pathlib import Path Path('out').write_text('stray file') try: os.makedirs('out') except FileExistsError: pass # 'already there, fine' os.path.isdir('out')
False
exist_ok=True
import os from pathlib import Path Path('out').write_text('stray file') try: os.makedirs('out', exist_ok=True) r = 'ready' except FileExistsError: r = 'out is a file, not a folder' r
'out is a file, not a folder'
When to use
Use it
- Refusing to overwrite: open(p, 'x') and catch FileExistsError
- Lock files and "first one wins" creation
- Raising it from your own create-only API
Reach for something else
- Folder may already exist → os.makedirs(p, exist_ok=True)
- Overwrite on purpose → os.replace(src, dst)
- Just checking → Path.exists()
Notes
CPython impl
Objects/exceptions.c — OSError(EEXIST, …) returns FileExistsError via the errno map
Catch via
except OSError
Platforms
os.rename onto an existing file raises FileExistsError on Windows but replaces silently on Linux/macOS; os.replace behaves the same everywhere
FAQ
Use os.makedirs(path, exist_ok=True) or Path(path).mkdir(parents=True, exist_ok=True). Both succeed when the directory is already there and still raise if something else is wrong.
History
3.3
Added, together with the other OSError subclasses (PEP 3151).