RustPython/RustPython · error · ValueError

raw stream already detached

Error message

raw stream already detached

What it means

Raised by _pyio.BufferedIOBase.detach (Lib/_pyio.py:823) when detach() is called on a buffered wrapper whose underlying raw stream was already removed. detach() extracts the raw stream and sets _raw = None; every subsequent detach() on the same wrapper raises ValueError('raw stream already detached'). After detaching, the wrapper is permanently unusable and only the returned raw object remains valid.

Source

Thrown at Lib/_pyio.py:823

    ### Flush and close ###

    def flush(self):
        if self.closed:
            raise ValueError("flush on closed file")
        self.raw.flush()

    def close(self):
        if self.raw is not None and not self.closed:
            try:
                # may raise BlockingIOError or BrokenPipeError etc
                self.flush()
            finally:
                self.raw.close()

    def detach(self):
        if self.raw is None:
            raise ValueError("raw stream already detached")
        self.flush()
        raw = self._raw
        self._raw = None
        return raw

    ### Inquiries ###

    def seekable(self):
        return self.raw.seekable()

    @property
    def raw(self):
        return self._raw

    @property
    def closed(self):
        return self.raw.closed

View on GitHub (pinned to aaeab4f754)

Solutions

  1. Call detach() exactly once and keep the returned raw object; drop the dead wrapper reference
  2. If you need to replace a standard stream, assign a whole new wrapper to sys.stdout instead of detaching the old one
  3. Guard the call: only detach when buf.raw is not None
  4. Record detach state (e.g. set a flag) when helpers may detach indirectly

Example fix

// before
raw1 = buf.detach()
# ... later ...
raw2 = buf.detach()  # ValueError: raw stream already detached

// after
raw1 = buf.detach()
# operate on raw1 from now on; buf is spent — do not touch it again
Defensive patterns

Strategy: validation

Validate before calling

if buf.raw is not None:
    raw = buf.detach()

Type guard

def is_detached(buf) -> bool:
    return buf.raw is None

Try / catch

try:
    raw = buf.detach()
except ValueError as e:
    if "raw stream already detached" in str(e):
        raw = None  # was already detached earlier
    else:
        raise

Prevention

When it happens

Trigger: Calling b.detach() twice on the same BufferedReader/Writer/Random; calling detach() after code that already detached (e.g. a helper that unwraps streams); reconfiguration routines that detach sys.stdout's TextIOWrapper and then run again.

Common situations: Swapping sys.stdout/sys.stderr by detaching the existing wrapper; generic 'unwrap the raw stream' utilities invoked multiple times in tests; mixing TextIOWrapper.detach() with atexit reconfiguration.

Related errors


AI-assisted analysis of RustPython/RustPython@aaeab4f754 (2026-08-17). Data as JSON: /api/errors/880d98ff40bd102f. Report an issue: GitHub.