langchain-ai/deepagents · error · RuntimeError

Textual's LinuxDriver no longer stores its output stream in

Error message

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.

What it means

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.

Source

Thrown at libs/code/deepagents_code/_terminal_stderr.py:96

        def __init__(
            self,
            app: App,
            *,
            debug: bool = False,
            mouse: bool = True,
            size: tuple[int, int] | None = None,
        ) -> None:
            super().__init__(app, debug=debug, mouse=mouse, size=size)
            if not hasattr(self, "_file"):
                # Textual moved its output stream. Fail loudly while the
                # caller's `finally` can still restore fd 2 to report it —
                # assigning a dead attribute would leave a black screen.
                msg = (
                    "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."
                )
                raise RuntimeError(msg)
            self._file = stdout

    return StdoutLinuxDriver


class TerminalStderrGuard:
    """Suppress native stderr writes while the TUI owns the terminal."""

    def __init__(self, *, enabled: bool = False) -> None:
        self._enabled = enabled
        self._saved_stderr: int | None = None
        self._closed = False
        self._lock = threading.Lock()

    @classmethod
    def install(cls) -> TerminalStderrGuard:
        """Install suppression when stdout and stderr share a macOS terminal.

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Pin Textual to the version range supported by deepagents-code (downgrade, e.g. `uv pip install 'textual==<pinned-version>'`, or regenerate the lockfile).
  2. 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).
  3. Report/track the incompatibility upstream so the guard is updated for the new Textual release.

Example fix

# before (patched driver, old internals)
if not hasattr(self, "_file"):
    raise RuntimeError("Textual's LinuxDriver no longer stores its output stream in `_file` ...")
# after
attr = "_file" if hasattr(self, "_file") else "_output"  # new Textual internals
setattr(self, attr, stdout)
Defensive patterns

Strategy: fallback

Validate before calling

import textual, inspect
from textual.driver import Driver
def textual_compat() -> bool:
    src = inspect.getsource(Driver.__init__) if hasattr(Driver, "__init__") else ""
    # probe the patched driver type at import time
    from deepagents_code._terminal_stderr import StdoutLinuxDriver
    return "_file" in getattr(StdoutLinuxDriver, "__mro__", ()) and textual.VERSION.major_minor_ok  # simplify: check pinned range

Type guard

def supports_stderr_guard(driver_cls) -> bool:
    import inspect
    return "_file" in inspect.getsource(driver_cls.__init__)

Try / catch

try:
    app.run()
except RuntimeError as exc:
    if "LinuxDriver" in str(exc):
        pin_supported_textual_version()  # e.g. subprocess: pip install textual==<pin>
    else:
        raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.


AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29). Data as JSON: /api/errors/303dcbb67a954936. Report an issue: GitHub.