pytest-dev/pytest · error · UnsupportedOperation

Cannot write to stdin

Error message

Cannot write to stdin

What it means

Raised by DontReadFromInput.writelines() (src/_pytest/capture.py:276). Note the message capitalization ('Cannot') differs from write()'s 'cannot' at capture.py:274 - a pre-existing inconsistency. writelines() is rejected for the same reason as write(): stdin is read-only (writable() returns False).

Solutions

  1. Target sys.stdout.writelines() or sys.stderr.writelines() instead
  2. Guard with writable() before calling writelines()
  3. Run pytest with -s so sys.stdin is the real (still read-only) handle and the error surfaces from the OS layer with a clearer message
  4. try/except OSError around the call if it is non-critical

Example fix

// before
sys.stdin.writelines(lines)
// after
sys.stdout.writelines(lines)
Defensive patterns

Strategy: type-guard

Validate before calling

import sys
if not sys.stdin.writable():
    pass

Type guard

def is_writable(stream) -> bool:
    try:
        return stream.writable()
    except Exception:
        return False

Try / catch

from io import UnsupportedOperation
try:
    sys.stdin.writelines(lines)
except UnsupportedOperation:
    sys.stdout.writelines(lines)

Prevention

When it happens

Trigger: Calling sys.stdin.writelines(lines) on the captured pseudo-file. capture.py:276-277 raises unconditionally regardless of the iterable contents.

Common situations: Stream-duplication utilities, generic 'write to all standard handles' helpers, or libraries that batch-emit status to whichever stream they receive. Same class of bug as error 43 but via the batch API.

Related errors


AI-assisted analysis of pytest-dev/pytest@0d6fbdeffa (2026-08-11). Data as JSON: /api/errors/c3f16c1b9ef6fd1b. Report an issue: GitHub.

Appendix: source

Thrown at src/_pytest/capture.py:277

        return False

    def seek(self, offset: int, whence: int = 0) -> int:
        raise UnsupportedOperation("redirected stdin is pseudofile, has no seek(int)")

    def seekable(self) -> bool:
        return False

    def tell(self) -> int:
        raise UnsupportedOperation("redirected stdin is pseudofile, has no tell()")

    def truncate(self, size: int | None = None) -> int:
        raise UnsupportedOperation("cannot truncate stdin")

    def write(self, data: str) -> int:
        raise UnsupportedOperation("cannot write to stdin")

    def writelines(self, lines: Iterable[str]) -> None:
        raise UnsupportedOperation("Cannot write to stdin")

    def writable(self) -> bool:
        return False

    def __enter__(self) -> Self:
        return self

    def __exit__(
        self,
        type: type[BaseException] | None,
        value: BaseException | None,
        traceback: TracebackType | None,
    ) -> None:
        pass

    @property
    def buffer(self) -> BinaryIO:
        # The str/bytes doesn't actually matter in this type, so OK to fake.

View on GitHub (pinned to 0d6fbdeffa)