{"record":{"id":"303dcbb67a954936","repo":"langchain-ai/deepagents","slug":"textual-s-linuxdriver-no-longer-stores-its-output","errorCode":null,"errorMessage":"Textual's LinuxDriver no longer stores its output stream in `_file`, so the stderr guard cannot route the TUI to stdout. Update stdout_driver_class for the new internals.","messagePattern":"Textual's LinuxDriver no longer stores its output stream in `_file`, so the stderr guard cannot route the TUI to stdout\\. Update stdout_driver_class for the new internals\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"libs/code/deepagents_code/_terminal_stderr.py","lineNumber":96,"sourceCode":"        def __init__(\n            self,\n            app: App,\n            *,\n            debug: bool = False,\n            mouse: bool = True,\n            size: tuple[int, int] | None = None,\n        ) -> None:\n            super().__init__(app, debug=debug, mouse=mouse, size=size)\n            if not hasattr(self, \"_file\"):\n                # Textual moved its output stream. Fail loudly while the\n                # caller's `finally` can still restore fd 2 to report it —\n                # assigning a dead attribute would leave a black screen.\n                msg = (\n                    \"Textual's LinuxDriver no longer stores its output stream \"\n                    \"in `_file`, so the stderr guard cannot route the TUI to \"\n                    \"stdout. Update stdout_driver_class for the new internals.\"\n                )\n                raise RuntimeError(msg)\n            self._file = stdout\n\n    return StdoutLinuxDriver\n\n\nclass TerminalStderrGuard:\n    \"\"\"Suppress native stderr writes while the TUI owns the terminal.\"\"\"\n\n    def __init__(self, *, enabled: bool = False) -> None:\n        self._enabled = enabled\n        self._saved_stderr: int | None = None\n        self._closed = False\n        self._lock = threading.Lock()\n\n    @classmethod\n    def install(cls) -> TerminalStderrGuard:\n        \"\"\"Install suppression when stdout and stderr share a macOS terminal.\n","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/code/deepagents_code/_terminal_stderr.py#L78-L114","documentation":"TerminalStderrGuard patches Textual's LinuxDriver to capture its output stream and route the TUI to stdout when stderr is being used. The guard checks for the `_file` attribute inside LinuxDriver.__init__; if a Textual upgrade removed/renamed it, the patched driver raises RuntimeError rather than assigning a dead attribute and leaving a black screen.","triggerScenarios":"Launching the deepagents-code TUI (StdoutLinuxDriver.__init__ path) with an installed Textual version whose LinuxDriver no longer stores its output stream in self._file. The check is invoked during driver construction.","commonSituations":"Upgrading Textual (e.g. a dependency bump pulled a newer Textual with refactored driver/output internals) while deepagents-code pins or assumes older internals; using a Textual pre-release/dev version.","solutions":["Pin Textual to the version range supported by deepagents-code (downgrade, e.g. `uv pip install 'textual==<pinned-version>'`, or regenerate the lockfile).","Update `stdout_driver_class` in deepagents_code/_terminal_stderr.py to attach to the new Textual internals (whatever replaced `_file`, e.g. the new output/stream attribute).","Report/track the incompatibility upstream so the guard is updated for the new Textual release."],"exampleFix":"# before (patched driver, old internals)\nif not hasattr(self, \"_file\"):\n    raise RuntimeError(\"Textual's LinuxDriver no longer stores its output stream in `_file` ...\")\n# after\nattr = \"_file\" if hasattr(self, \"_file\") else \"_output\"  # new Textual internals\nsetattr(self, attr, stdout)","handlingStrategy":"fallback","validationCode":"import textual, inspect\nfrom textual.driver import Driver\ndef textual_compat() -> bool:\n    src = inspect.getsource(Driver.__init__) if hasattr(Driver, \"__init__\") else \"\"\n    # probe the patched driver type at import time\n    from deepagents_code._terminal_stderr import StdoutLinuxDriver\n    return \"_file\" in getattr(StdoutLinuxDriver, \"__mro__\", ()) and textual.VERSION.major_minor_ok  # simplify: check pinned range\n","typeGuard":"def supports_stderr_guard(driver_cls) -> bool:\n    import inspect\n    return \"_file\" in inspect.getsource(driver_cls.__init__)","tryCatchPattern":"try:\n    app.run()\nexcept RuntimeError as exc:\n    if \"LinuxDriver\" in str(exc):\n        pin_supported_textual_version()  # e.g. subprocess: pip install textual==<pin>\n    else:\n        raise","preventionTips":["Pin the Textual version in your lockfile and only upgrade together with deepagents-code.","Run a smoke test that launches the TUI in CI after any dependency bump.","Read Textual's driver changelog before upgrading major/minor versions."],"tags":["textual","tui","version-incompatibility","terminal"],"backgroundTag":"library-internals-changed","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}