pytest-dev/pytest · error · UnsupportedOperation

cannot write to stdin

Error message

cannot write to stdin

What it means

Raised by DontReadFromInput.write() (src/_pytest/capture.py:273). Writing to stdin is semantically wrong; the redirected pseudo-file is read-only and writable() at capture.py:279 returns False. pytest raises UnsupportedOperation (lowercase 'cannot') to surface the misuse rather than silently drop data.

Solutions

  1. Route the write to the correct stream (sys.stdout or sys.stderr) instead of sys.stdin
  2. Guard with `if sys.stdin.writable():` to short-circuit on the pseudo-file
  3. Disable capture with -s if the write is intentional in your test harness
  4. try/except OSError and ignore the dropped write

Example fix

// before
sys.stdin.write('data')
// after
sys.stdout.write('data')
Defensive patterns

Strategy: type-guard

Validate before calling

import sys
if not sys.stdin.writable():
    raise ValueError('refusing to write to read-only stdin')

Type guard

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

Try / catch

from io import UnsupportedOperation
try:
    sys.stdin.write(data)
except UnsupportedOperation:
    # Redirect instead
    sys.stdout.write(data)

Prevention

When it happens

Trigger: Calling sys.stdin.write(data) in a test while default capture is active. capture.py:273-274 raises unconditionally; writable() returns False at capture.py:279 so callers that probe first are safe.

Common situations: Misimplemented stream multiplexers, mock/patch code that writes to 'all standard streams', or test scaffolding that mistakenly writes to sys.stdin instead of sys.stdout. Less common than read/seek errors.

Related errors


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

Appendix: source

Thrown at src/_pytest/capture.py:274

        pass

    def readable(self) -> bool:
        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

View on GitHub (pinned to 0d6fbdeffa)