python/cpython · error · TypeError

expected integer from opener

Error message

expected integer from opener

What it means

Raised by FileIO.__init__ when a custom opener callable returns a value that is not an int. The opener protocol (PEP 443-era feature used by open(file, opener=...)) requires opener(path, flags) -> int fd; anything else (float, None, a tuple, an int subclass like bool aside) is rejected with TypeError before the fd is used.

Source

Thrown at Lib/_pyio.py:1610

            flags |= os.O_WRONLY

        flags |= getattr(os, 'O_BINARY', 0)

        noinherit_flag = (getattr(os, 'O_NOINHERIT', 0) or
                          getattr(os, 'O_CLOEXEC', 0))
        flags |= noinherit_flag

        owned_fd = None
        try:
            if fd < 0:
                if not closefd:
                    raise ValueError('Cannot use closefd=False with file name')
                if opener is None:
                    fd = os.open(file, flags, 0o666)
                else:
                    fd = opener(file, flags)
                    if not isinstance(fd, int):
                        raise TypeError('expected integer from opener')
                    if fd < 0:
                        # bpo-27066: Raise a ValueError for bad value.
                        raise ValueError(f'opener returned {fd}')
                owned_fd = fd
                if not noinherit_flag:
                    os.set_inheritable(fd, False)

            self._closefd = closefd
            self._stat_atopen = os.fstat(fd)
            try:
                if stat.S_ISDIR(self._stat_atopen.st_mode):
                    raise IsADirectoryError(errno.EISDIR,
                                            os.strerror(errno.EISDIR), file)
            except AttributeError:
                # Ignore the AttributeError if stat.S_ISDIR or errno.EISDIR
                # don't exist.
                pass

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Make the opener return the raw int fd: def opener(path, flags): return os.open(path, flags, 0o600)
  2. Return the fd on every code path, including error paths (or raise OSError yourself instead of returning a sentinel)
  3. If delegating, unwrap the integer: return wrapper.fileno()

Example fix

# before
def opener(path, flags):
    os.open(path, flags)          # forgot return -> returns None
f = open('f.bin', 'wb', opener=opener)  # TypeError: expected integer from opener

# after
def opener(path, flags):
    return os.open(path, flags, 0o600)
f = open('f.bin', 'wb', opener=opener)
Defensive patterns

Strategy: type-guard

Validate before calling

fd = opener(file, flags)
if not isinstance(fd, int) or isinstance(fd, bool):
    raise TypeError('expected integer from opener')
if fd < 0:
    raise ValueError(f'opener returned {fd}')

Type guard

def opener_returns_int(fn):
    # smoke-test the opener once with a harmless call signature in tests
    return callable(fn)

Try / catch

try:
    f = open(path, 'wb', opener=opener)
except TypeError as e:
    if 'opener' in str(e):
        def fixed_opener(p, flags):
            return int(opener(p, flags))
        f = open(path, 'wb', opener=fixed_opener)
    else:
        raise

Prevention

When it happens

Trigger: open(path, 'wb', opener=lambda p, f: os.open(p, f) if cond else None); an opener returning a numpy integer or a dataclass wrapping the fd; an opener that forgets to return and yields None.

Common situations: Custom openers implementing sandboxing/permission checks that return a status code or (fd, error) tuple instead of a bare fd; openers delegating to wrappers (e.g. os.fdopen results, which are file objects, not ints); missing return statement after an early-exit branch.

Related errors


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/5ad19edee94ba985. Report an issue: GitHub.