OSError [Errno 22] Invalid argument (tested)

OSError: [Errno 22] Invalid argument means the operating system rejected a value Python passed it. We reproduced four causes on Python 3.13.5, including the filename bug that only shows up on Windows.

1. A filename with a colon (works on Linux, not on Windows)

import datetime, os, re
stamp = datetime.datetime(2026, 10, 9, 14, 30, 0)
name = f"report {stamp}.txt"
print(repr(name))
open(name, "w").close()          # works on Linux; ':' is not allowed on Windows
print("created on Linux:", os.path.exists(name))

def safe_filename(s):
    return re.sub(r'[<>:"/\\|?*\x00-\x1f]', "_", s).rstrip(". ")

print(repr(safe_filename(name)))
print(repr(f"report {stamp:%Y-%m-%d_%H-%M-%S}.txt"))

Output:

'report 2026-10-09 14:30:00.txt'
created on Linux: True
'report 2026-10-09 14_30_00.txt'
'report 2026-10-09_14-30-00.txt'

Measured on Python 3.13.5 on Linux: a filename built from str(datetime) contains colons, and Linux creates it without complaint. Windows doesn't allow <>:"/\|?* in names (a common cause of errno 22 there), so code tested on Linux or macOS can fail the first time it runs on Windows. (We couldn't run the Windows side on this Linux box; what we measured is that Linux accepts the name.) Format timestamps with - or _, or pass user-supplied names through a function like safe_filename.

2. Seeking to a negative position

import os
with open("f.txt", "rb") as f:
    try:
        f.seek(-10)               # absolute position before the start
    except OSError as e:
        print("seek(-10):", e)
    print("seek(-3, 2):", f.seek(-3, 2), f.read())   # relative to the end: fine
with open("f.txt") as f:
    try:
        f.seek(-3, 2)             # text mode: only seek(0, 2) is allowed
    except Exception as e:
        print("text seek(-3, 2):", type(e).__name__, e)

Output:

seek(-10): [Errno 22] Invalid argument
seek(-3, 2): 3 b'lo\n'
text seek(-3, 2): UnsupportedOperation can't do nonzero end-relative seeks

Measured: seek(-10) means "absolute position -10", which is errno 22. To read the end of a file, seek relative to the end with whence=2, in binary mode. Text mode refuses any non-zero end-relative seek with a different error, UnsupportedOperation.

3. Moving a directory into itself

import os, shutil
os.makedirs("data", exist_ok=True)
try:
    os.rename("data", "data/backup")
except OSError as e:
    print("os.rename:", e)
try:
    shutil.move("data", "data/backup")
except Exception as e:
    print("shutil.move:", type(e).__name__, e)

Output:

os.rename: [Errno 22] Invalid argument: 'data' -> 'data/backup'
shutil.move: Error Cannot move a directory 'data' into itself 'data/backup'.

Measured: os.rename reports this as a bare errno 22 with both paths; shutil.move checks first and says what's wrong. shutil.Error is an OSError subclass, so except OSError catches both.

4. Negative sizes and impossible values in os calls

import os
fd = os.open("f.txt", os.O_RDONLY)
for name, call in [("os.read(fd, -1)", lambda: os.read(fd, -1)),
                   ("os.truncate(path, -1)", lambda: os.truncate("f.txt", -1)),
                   ("os.sched_setaffinity(0, {999})", lambda: os.sched_setaffinity(0, {999}))]:
    try:
        call()
    except OSError as e:
        print(f"{name:32} -> errno {e.errno}: {e}")

Output:

os.read(fd, -1)                  -> errno 22: [Errno 22] Invalid argument
os.truncate(path, -1)            -> errno 22: [Errno 22] Invalid argument: 'f.txt'
os.sched_setaffinity(0, {999})   -> errno 22: [Errno 22] Invalid argument

Measured: the os module passes values to the kernel as they are, and the kernel answers EINVAL: a negative read size, a negative length, a CPU that doesn't exist. Print the arguments right before the failing call; one is usually negative, zero or out of range from an earlier calculation. Other errno numbers are in our errno codes list.

Frequently asked questions

What does OSError: [Errno 22] Invalid argument mean in Python?

The operating system rejected a value you passed. We measured it on Linux from seek(-10), os.rename of a directory into itself, os.read(fd, -1) and os.truncate(path, -1).

Why do I get Errno 22 on Windows but not on Linux?

Often a filename with a character Windows doesn't allow, such as the colons in str(datetime). We measured Linux creating 'report 2026-10-09 14:30:00.txt' without error.

How do I make a timestamp safe for a filename?

Format it without colons, e.g. f'{stamp:%Y-%m-%d_%H-%M-%S}', or replace <>:"/\|?* with underscores. We measured both.

Why does f.seek give Invalid argument?

A negative absolute position. We measured seek(-10) raising errno 22 while seek(-3, 2) in binary mode read the last 3 bytes.

Written with AI assistance. Measured on 2026-10-09 with Python 3.13.5 on Linux: a datetime-based filename, negative and end-relative seeks in binary and text mode, os.rename vs shutil.move into itself, and negative os.read/os.truncate values. Every block was run verbatim and its output pasted from that run. PlainHeaders is our extension; everything else here is built-in Python.