python/cpython · warning · OSError

ENOTTY

ENOTTY

Error message

tty required

What it means

OSError with errno ENOTTY raised at import time of _pyrepl.main when sys.stdin is not a TTY (piped input, redirected file, or non-terminal stream). Like the Windows check, it is raised inside a try/except that swallows it: CAN_USE_PYREPL becomes False and FAIL_REASON records the message, so Python silently falls back to the plain REPL/read-from-stdin behavior.

Source

Thrown at Lib/_pyrepl/main.py:13

import errno
import os
import sys
import types


CAN_USE_PYREPL: bool
FAIL_REASON: str
try:
    if sys.platform == "win32" and sys.getwindowsversion().build < 10586:
        raise RuntimeError("Windows 10 TH2 or later required")
    if not os.isatty(sys.stdin.fileno()):
        raise OSError(errno.ENOTTY, "tty required", "stdin")
    from .simple_interact import check
    if err := check():
        raise RuntimeError(err)
except Exception as e:
    CAN_USE_PYREPL = False
    FAIL_REASON = f"warning: can't use pyrepl: {e}"
else:
    CAN_USE_PYREPL = True
    FAIL_REASON = ""


def interactive_console(mainmodule=None, quiet=False, pythonstartup=False):
    if not CAN_USE_PYREPL:
        if not os.getenv('PYTHON_BASIC_REPL') and FAIL_REASON:
            from .trace import trace
            trace(FAIL_REASON)
            print(FAIL_REASON, file=sys.stderr)
        return sys._baserepl()

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. This is expected behavior, not a bug — feed a real terminal. Run python in a PTY (pty.openpty, pexpect, or 'script -qc "python" /dev/null') when embedding.
  2. For scripted input, don't use the interactive REPL at all: pass the script path or use python -c / stdin as a file (python file.py), which never imports _pyrepl.main.
  3. Set PYTHON_BASIC_REPL=1 to skip the pyrepl check and message if the warning is noise.

Example fix

# before
# cat cmds.txt | python -i   -> warning: can't use pyrepl: tty required

# after
# python -i cmds.txt        -> runs script then interactive, no pyrepl warning
# or embed with a pty:
import pexpect
pexpect.spawn('python')  # gives the child a real tty
Defensive patterns

Strategy: validation

Validate before calling

import os, sys

def repl_possible():
    try:
        return os.isatty(sys.stdin.fileno())
    except (OSError, ValueError):
        return False

Prevention

When it happens

Trigger: Importing _pyrepl.main (implicitly via the interactive interpreter) while stdin is a pipe or file: python < script.py in interactive mode, python -i fed by a pipe, REPL launched inside process substitution, or stdin closed. os.isatty(sys.stdin.fileno()) returning False is the exact trigger.

Common situations: Running the REPL under piping tools (cat file | python -i), debuggers/embedders that replace stdin, CI harnesses that open REPL sessions with redirected stdin, and terminal multiplexer misconfigurations. Only symptom is 'warning: can't use pyrepl: [Errno 25] tty required: stdin' at startup.

Related errors


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