A failed file syscall lost both its class and its message
Found 2026-08-16 while triaging Track T's regression-cascade-343a52551808:
the uforth corpus is diffed against CPython byte for byte, and a missing
INCLUDE printed the bare path.
Measured
open("/nonexistent/f.txt")
# CPython FileNotFoundError: [Errno 2] No such file or directory: '/nonexistent/f.txt'
# NilPy FileNotFoundError: /nonexistent/f.txt
open("/tmp", "w")
# CPython IsADirectoryError: [Errno 21] Is a directory: '/tmp'
# NilPy FileNotFoundError: /tmp <- wrong CLASS
try:
open("/nonexistent/f.txt")
except IOError as e: # the most common spelling of this guard
...
# CPython caught
# NilPy NOT caught
Cause, both halves
- Class. Every
PyPal*failure path raisedFileNotFoundError.Create(path)unconditionally.__pxxrawsyscallreturns the raw kernel value, so the errno was right there inrand simply not read. - Alias vs subclass.
IOError = class(OSError)makes IOError a SIBLING of FileNotFoundError, not an ancestor, soexcept IOError:misses it. CPython makes IOError and EnvironmentError aliases —IOError is OSErroris True. pylib's own comment said "aliases of OSError" while the line below it saidclass(OSError); the comment was right and the code was not.
Fix
pyos_raise_ioerror(err, path, path2) in pylib maps errno → subclass and builds
CPython's message (including the -> 'dst' tail for two-path calls), and the
four raise sites call it. IOError = OSError; / EnvironmentError = OSError;.
Why it matters
The message half is what a program PRINTS — nothing crashes and no value is
wrong, so only a differential corpus catches it. The class half is worse: a
program that distinguishes "not there" from "not allowed" takes the wrong
branch silently, and one that guards with except IOError: — which CPython
accepts and runs — does not catch at all. That is the upward-compatibility
direction that is not negotiable.
Gate
test/test_nilpy_oserror_class_and_message.npy, six shapes diffed against
CPython byte for byte; the uforth filetest word set identical to CPython;
gate.sh quick green.