shutil copy/move PermissionError: [Errno 13] (tested)
PermissionError: [Errno 13] Permission denied from shutil.copy() or shutil.move() has a few causes, and one of them only shows up on the second run of your script. We reproduced all of them on Python 3.13.5.
1. The destination directory isn't writable
import os, shutil
os.makedirs("ro_dir", exist_ok=True); os.chmod("ro_dir", 0o555)
open("src.txt", "w").write("data")
for label, call in [("copy into read-only dir", lambda: shutil.copy("src.txt", "ro_dir")),
("copy onto a directory path as file", lambda: shutil.copyfile("src.txt", "ro_dir")),
("move into read-only dir", lambda: shutil.move("src.txt", "ro_dir/src.txt"))]:
try:
call(); print(label, "-> ok")
except OSError as e:
print(f"{label} -> {type(e).__name__}: {e}")
os.chmod("ro_dir", 0o755)
Output:
copy into read-only dir -> PermissionError: [Errno 13] Permission denied: 'ro_dir/src.txt'
copy onto a directory path as file -> IsADirectoryError: [Errno 21] Is a directory: 'ro_dir'
move into read-only dir -> PermissionError: [Errno 13] Permission denied: 'ro_dir/src.txt'
Measured on Python 3.13.5, Linux: both shutil.copy() and shutil.move() need write permission on the destination directory, and the error names the full destination path (ro_dir/src.txt). copyfile() wants a file path, not a directory: give it a directory and you get IsADirectoryError (on Windows this case is reported as PermissionError instead, which is a common source of confusion; not measured here).
2. Copy over a read-only file fails, move over it works
import os, shutil
open("a.txt", "w").write("new")
open("b.txt", "w").write("old"); os.chmod("b.txt", 0o444) # read-only destination
try:
shutil.copy("a.txt", "b.txt")
except PermissionError as e:
print("copy over read-only file:", e)
shutil.move("a.txt", "b.txt") # rename replaces it: directory permission is what counts
print("move over read-only file: ok ->", open("b.txt").read())
Output:
copy over read-only file: [Errno 13] Permission denied: 'b.txt'
move over read-only file: ok -> new
Measured: copy() opens the existing destination for writing, so a read-only b.txt gives Errno 13. move() renames instead, and replacing a file by rename only needs permission on the directory, so it silently replaced the read-only file.
3. Why the second run fails: copy() copies the read-only bit
import os, shutil
open("s.txt", "w").write("data"); os.chmod("s.txt", 0o400)
shutil.copy("s.txt", "c1.txt"); shutil.copy2("s.txt", "c2.txt"); shutil.copyfile("s.txt", "c3.txt")
for f in ("c1.txt", "c2.txt", "c3.txt"):
print(f, oct(os.stat(f).st_mode & 0o777))
try:
shutil.copy("s.txt", "c1.txt")
except PermissionError as e:
print("second copy:", e)
Output:
c1.txt 0o400
c2.txt 0o400
c3.txt 0o664
second copy: [Errno 13] Permission denied: 'c1.txt'
Measured: shutil.copy() and copy2() copy the source's permission bits, so copying a read-only source (0o400) produces a read-only copy, and the next copy onto it fails. copyfile() copies only the contents and the new file gets normal permissions (0o664 here, from the umask). This is the usual cause of a script that works once and then fails on every rerun.
Fix: copy contents only, and make an existing destination writable first
import os, shutil
def copy_over(src, dst):
if os.path.exists(dst) and not os.access(dst, os.W_OK):
os.chmod(dst, os.stat(dst).st_mode | 0o200) # make destination writable
shutil.copyfile(src, dst) # contents only, keeps dst's mode
copy_over("s.txt", "c1.txt"); copy_over("s.txt", "c1.txt")
print("c1.txt", oct(os.stat("c1.txt").st_mode & 0o777), open("c1.txt").read())
Output:
c1.txt 0o600 data
Measured: running it twice over the read-only c1.txt works. Only do the chmod on files you own and mean to overwrite. For the directory case (section 1), fix the permissions or copy somewhere you can write; on Windows also check that no other program has the file open. For deleting read-only trees see our rmtree PermissionError guide.
Frequently asked questions
Why does shutil.copy give PermissionError: [Errno 13]?
Either the destination directory isn't writable or the destination file is read-only. We measured both on Linux with Python 3.13.5.
Why does shutil.copy work once and then fail?
copy() and copy2() copy the source's permission bits, so a read-only source makes a read-only copy and the next copy onto it fails. We measured that; copyfile() copies contents only.
Can shutil.move overwrite a read-only file?
Yes, on Linux. We measured move() replacing a 0o444 file, because a rename only needs write permission on the directory.
What is the difference between shutil.copy, copy2 and copyfile?
copyfile copies contents only; copy also copies permission bits; copy2 also copies timestamps. We measured copy and copy2 giving 0o400 copies of a 0o400 file and copyfile giving 0o664.
Why does shutil.copyfile say Is a directory?
copyfile needs a destination file path, not a folder. We measured IsADirectoryError: [Errno 21] for a directory destination.
Written with AI assistance. Measured on 2026-10-09 with Python 3.13.5 on Linux: copy/copyfile/move into a read-only directory, copy vs move over a read-only file, permission bits after copy/copy2/copyfile from a 0o400 source, and a copy-over fix. Every block was run verbatim and its output pasted from that run. PlainHeaders is our extension; everything else here is built-in Python.