{"record":{"id":"880d98ff40bd102f","repo":"RustPython/RustPython","slug":"raw-stream-already-detached","errorCode":null,"errorMessage":"raw stream already detached","messagePattern":"raw stream already detached","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"Lib/_pyio.py","lineNumber":823,"sourceCode":"\n    ### Flush and close ###\n\n    def flush(self):\n        if self.closed:\n            raise ValueError(\"flush on closed file\")\n        self.raw.flush()\n\n    def close(self):\n        if self.raw is not None and not self.closed:\n            try:\n                # may raise BlockingIOError or BrokenPipeError etc\n                self.flush()\n            finally:\n                self.raw.close()\n\n    def detach(self):\n        if self.raw is None:\n            raise ValueError(\"raw stream already detached\")\n        self.flush()\n        raw = self._raw\n        self._raw = None\n        return raw\n\n    ### Inquiries ###\n\n    def seekable(self):\n        return self.raw.seekable()\n\n    @property\n    def raw(self):\n        return self._raw\n\n    @property\n    def closed(self):\n        return self.raw.closed\n","sourceCodeStart":805,"sourceCodeEnd":841,"githubUrl":"https://github.com/RustPython/RustPython/blob/aaeab4f754b4f40efc0c8ab39cf7c4a3c35a8cfd/Lib/_pyio.py#L805-L841","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Call detach() exactly once and keep the returned raw object; drop the dead wrapper reference","If you need to replace a standard stream, assign a whole new wrapper to sys.stdout instead of detaching the old one","Guard the call: only detach when buf.raw is not None","Record detach state (e.g. set a flag) when helpers may detach indirectly"],"exampleFix":"// before\nraw1 = buf.detach()\n# ... later ...\nraw2 = buf.detach()  # ValueError: raw stream already detached\n\n// after\nraw1 = buf.detach()\n# operate on raw1 from now on; buf is spent — do not touch it again","handlingStrategy":"validation","validationCode":"if buf.raw is not None:\n    raw = buf.detach()","typeGuard":"def is_detached(buf) -> bool:\n    return buf.raw is None","tryCatchPattern":"try:\n    raw = buf.detach()\nexcept ValueError as e:\n    if \"raw stream already detached\" in str(e):\n        raw = None  # was already detached earlier\n    else:\n        raise","preventionTips":["Call detach() exactly once and keep the returned raw object","After detaching, operate on the raw object — treat the wrapper as dead","When swapping sys.stdout, assign a new wrapper rather than detaching the old one"],"tags":["python","io","buffered-io","detach","stream-lifecycle"],"backgroundTag":"detached-stream-misuse","analyzedSha":"aaeab4f754b4f40efc0c8ab39cf7c4a3c35a8cfd","analyzedAt":"2026-08-17T00:37:52.100Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}