Issue with Path.copy

Hello everyone. I am new here and I am looking for help with an issue that I am having with copying files using pathlib. I’m running python3.14 on Linux Mint.

When I try to use the .copy method on pathlib’s Path class to copy a file, it raises an OSError[22]. So far it looks like this happens when trying to copy a non-empty file. Here is the script that I am using to reproduce the error.

#!/usr/bin/env python3

from pathlib import Path

SRC = Path(__file__).parent / 'test1.txt'
DEST = Path(__file__).parent / 'test2.txt'

SRC.copy(DEST)

The contents of the file test1.txt are below:

123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789

And here is the traceback that I get from running the code.

Traceback (most recent call last):
  File "/home/robert/src/Sandbox/Test/./copy.py", line 8, in <module>
    SRC.copy(DEST)
  File "/usr/lib/python3.14/pathlib/__init__.py", line 1103, in copy
    target._copy_from(self, **kwargs)
  File "/usr/lib/python3.14/pathlib/__init__.py", line 1134, in _copy_from
    self._copy_from_file(source, preserve_metadata)
  File "/usr/lib/python3.14/pathlib/__init__.py", line 1140, in _copy_from_file
    copyfileobj(source_f, target_f)
  File "/usr/lib/python3.14/pathlib/_os.py", line 160, in copyfileobj
    raise err
  File "/usr/lib/python3.14/pathlib/_os.py", line 148, in copyfileobj
    raise err
  File "/usr/lib/python3.14/pathlib/_os.py", line 144, in copyfileobj
    _copy_file_range(source_fd, target_fd)
  File "/usr/lib/python3.14/pathlib/_os.py", line 78, in _copy_file_range
    sent = os.copy_file_range(source_fd, target_fd, blocksize,
OSError: [Errno 22] Invalid argument: '/home/robert/src/Sandbox/Test/test1.txt' -> '/home/robert/src/Sandbox/Test/test2.txt'

It seems to work fine when I use shutil.copy2 instead. Does anyone know why this might be occurring?

Cheers,

-Robert

Also using Python 3.14 on Linux Mint, it works for me.

Hello,

I don’t believe that the pathlib library currently has a dedicated copy built in function for copying files. This is why you are getting the OSError: [Errno 22] Invalid argument.

If you are using an editor like PyCharm, it will autodetect the available functions and classes available to you once adding a “.” dot after the keyword, you’ll notice that the copy keyword does not appear.

Update:
I am using version: 3.13.0 - So this might have something to do with it.

It was added in Python 3.14.

@MRAB

Thanks. Yes, I just did a search and I came to this conclusion. Nice to know that the pathlib library is adding this functionality.

Your program runs fine for me on my copy of 3.14, so I don’t see anything in your source that seems suspicious.

Odd to me is that the os.copy_file_range() call seems to want a fd, but the error shows that it received paths instead.

When I set a breakpoint there on my copy (which works), it does appear to be passing fds…

(Pdb) b /usr/lib/python3.14/pathlib/_os.py:78
Breakpoint 1 at /usr/lib/python3.14/pathlib/_os.py:78
(Pdb) c
> /usr/lib/python3.14/pathlib/_os.py(78)_copy_file_range()
-> sent = os.copy_file_range(source_fd, target_fd, blocksize,
(Pdb) p source_fd, target_fd
(3, 4)

So not sure why that’s not working in your run…..

Interesting. If you don’t mind, would you let me know what minor version you’re on and how you installed your python? I got mine through the deadsnakes ppa: https://launchpad.net/~deadsnakes/+archive/ubuntu/ppa.

I’m on WSL2 / ubuntu and fired up a new UV environment and pinned python to 3.14. Looks like that got me 3.14.2

:/tmp/test/src$ uv run python -V
Python 3.14.2

Thanks for checking it out. That is interesting. I did notice that os.copy_file_range() seemed to be raising the exception. It certainly seems odd since it does look like its supposed to take fds.

And i’m not sure if the “→” is part of the argument or not. I wondered if a symlink might be getting misinterpreted somewhere and that arrow is part of it (or if it’s just the error showing source and dest). If the very last path is a symlink it had no problem. But I didn’t test if maybe another component could be doing something odd.

Looks like I’m on 3.14.2 as well. I’ll try installing from a source other than deadsnakes and see if the issue persists when I have a moment. Thank you again for giving this a look.

Reading the man page for copy_file_range(2), Ernno 22 = EINVAL can be producded in three cases:

       EINVAL The flags argument is not 0.

       EINVAL fd_in and fd_out refer to the same file and the source and
              target ranges overlap.

       EINVAL Either fd_in or fd_out is not a regular file.

Based on the message you are seeing (which AFAICT is the result of strerror, not anything python tried to guess), I think case 3 is the one that actually happened, so either of those two files is not a regular file. Is one of them a symlink or something like this? If you delete both of them and then recreate the src file, does this still happen?

I tried lots of things on my machine to regenerate the error, but “non-regular” files didn’t do it for me (generated other errors). Only thing that came close for me was either hard- or soft- linking the source and dest together. That got a similar error 22, but with slightly different text. (Could be due to me being on a different Linux distro).

OSError: [Errno 22] Source and target are the same file: '/tmp/test/src/test1.txt' -> '/tmp/test/src/test2.txt'

I wonder if this is the case for the OP, but the OS is giving a different error.