Input box filled with text on startup #3856

Open
opened 2026-02-16 17:41:42 -05:00 by yindo · 5 comments
Owner

Originally created by @ocelot2123 on GitHub (Dec 25, 2025).

Originally assigned to: @kommander on GitHub.

Description

I am using Windows Powershell SSHing into Ubuntu Server 22.04.5. These texts are filled in the input box when running with opencode.
Sometimes it says other text, but I'm not able to reproduce that consistently.

Image

OpenCode version

1.0.2

Steps to reproduce

No response

Screenshot and/or share link

No response

Operating System

Windows 11 SSH into Ubuntu Server 22.04.5

Terminal

Windows PowerShell

Originally created by @ocelot2123 on GitHub (Dec 25, 2025). Originally assigned to: @kommander on GitHub. ### Description I am using Windows Powershell SSHing into Ubuntu Server 22.04.5. These texts are filled in the input box when running with `opencode`. Sometimes it says other text, but I'm not able to reproduce that consistently. <img width="1130" height="488" alt="Image" src="https://github.com/user-attachments/assets/0ebace76-563f-4072-bd55-23d0a4f04445" /> ### OpenCode version 1.0.2 ### Steps to reproduce _No response_ ### Screenshot and/or share link _No response_ ### Operating System Windows 11 SSH into Ubuntu Server 22.04.5 ### Terminal Windows PowerShell
yindo added the windowsopentuibug labels 2026-02-16 17:41:42 -05:00
Author
Owner

@github-actions[bot] commented on GitHub (Dec 25, 2025):

This issue might be a duplicate of existing issues. Please check:

  • #6119: [Bug] TUI fails to render with raw ANSI output and critical memory leak (72.5G VIRT) on startup - similar issue with text/ANSI codes appearing on startup over SSH with Linux servers
  • #6136: On closure terminal becomes window becomes bugged - related issue with ANSI escape sequences appearing in terminal after/during opencode usage

Feel free to ignore if none of these address your specific case.

@github-actions[bot] commented on GitHub (Dec 25, 2025): This issue might be a duplicate of existing issues. Please check: - #6119: [Bug] TUI fails to render with raw ANSI output and critical memory leak (72.5G VIRT) on startup - similar issue with text/ANSI codes appearing on startup over SSH with Linux servers - #6136: On closure terminal becomes window becomes bugged - related issue with ANSI escape sequences appearing in terminal after/during opencode usage Feel free to ignore if none of these address your specific case.
Author
Owner

@bbartels commented on GitHub (Feb 5, 2026):

Having the same issue, is there any workarounds?

@bbartels commented on GitHub (Feb 5, 2026): Having the same issue, is there any workarounds?
Author
Owner

@KyungmoKu commented on GitHub (Feb 9, 2026):

Had the same issue - Windows Terminal SSH into Ubuntu.

This worked for me: a small Python script that wraps opencode and filters out the garbage from stdin before it reaches the TUI.

  • Save below file as osc-filter (no .py) somewhere in your PATH, chmod +x osc-filter, then run osc-filter opencode instead of opencode.
  • You can alias it in your bashrc for convenience: alias oc='osc-filter opencode'

The proper fix probably belongs in opentui's input parser - it doesn't handle OSC sequences at all right now.

osc-filter
#!/usr/bin/env python3
"""PTY wrapper that filters OSC escape sequences from stdin.

Windows Terminal sends unsolicited OSC queries (10/11/4) over SSH.
The TUI's input parser has no OSC response handler, so the responses
appear as garbage text like "eeee/eeee/eeee" in the input field.

This wrapper runs the child command in a real PTY and strips OSC
sequences from stdin before forwarding them to the child.

Usage:
    osc-filter opencode [args...]

OSC sequence format:
    ESC ] ... BEL        (\x1b] ... \x07)
    ESC ] ... ESC \      (\x1b] ... \x1b\\)
"""

import fcntl
import os
import pty
import select
import signal
import sys
import termios

ESC = 0x1B
BEL = 0x07
BACKSLASH = ord("\\")

# Stateful: tracks whether we're inside a partial OSC sequence that was
# split across read() boundaries (e.g., SSH delivers the response in chunks).
_in_osc = False


def filter_osc(buf: bytes) -> bytes:
    """Remove OSC sequences from a byte buffer.

    Handles split reads: if an OSC sequence spans two read() calls,
    the second half is suppressed until the terminator is found.
    """
    global _in_osc
    out = bytearray()
    i = 0
    n = len(buf)

    # Continuing a partial OSC from the previous read
    if _in_osc:
        while i < n:
            if buf[i] == BEL:
                i += 1
                _in_osc = False
                break
            if buf[i] == ESC and i + 1 < n and buf[i + 1] == BACKSLASH:
                i += 2
                _in_osc = False
                break
            i += 1
        if _in_osc:
            return b""

    while i < n:
        if buf[i] == ESC and i + 1 < n and buf[i + 1] == ord("]"):
            i += 2
            while i < n:
                if buf[i] == BEL:
                    i += 1
                    break
                if buf[i] == ESC and i + 1 < n and buf[i + 1] == BACKSLASH:
                    i += 2
                    break
                i += 1
            else:
                _in_osc = True
        else:
            out.append(buf[i])
            i += 1

    return bytes(out)


def copy_window_size(from_fd: int, to_fd: int) -> None:
    try:
        size = fcntl.ioctl(from_fd, termios.TIOCGWINSZ, b"\x00" * 8)
        fcntl.ioctl(to_fd, termios.TIOCSWINSZ, size)
    except OSError:
        pass


def main() -> int:
    if len(sys.argv) < 2:
        print(f"Usage: {sys.argv[0]} <command> [args...]", file=sys.stderr)
        return 1

    if not os.isatty(sys.stdin.fileno()):
        os.execvp(sys.argv[1], sys.argv[1:])

    pid, master_fd = pty.fork()

    if pid == 0:
        os.execvp(sys.argv[1], sys.argv[1:])

    stdin_fd = sys.stdin.fileno()
    stdout_fd = sys.stdout.fileno()

    old_attrs = termios.tcgetattr(stdin_fd)
    try:
        new_attrs = termios.tcgetattr(stdin_fd)
        new_attrs[0] = 0
        new_attrs[1] = 0
        new_attrs[2] &= ~(termios.CSIZE | termios.PARENB)
        new_attrs[2] |= termios.CS8
        new_attrs[3] = 0
        new_attrs[6][termios.VMIN] = 1
        new_attrs[6][termios.VTIME] = 0
        termios.tcsetattr(stdin_fd, termios.TCSAFLUSH, new_attrs)
    except termios.error:
        pass

    copy_window_size(stdin_fd, master_fd)

    def handle_sigwinch(signum, frame):
        copy_window_size(stdin_fd, master_fd)
        try:
            os.kill(pid, signal.SIGWINCH)
        except OSError:
            pass

    signal.signal(signal.SIGWINCH, handle_sigwinch)

    try:
        while True:
            try:
                rfds, _, _ = select.select([stdin_fd, master_fd], [], [])
            except InterruptedError:
                continue

            if stdin_fd in rfds:
                try:
                    data = os.read(stdin_fd, 4096)
                except OSError:
                    break
                if not data:
                    break
                filtered = filter_osc(data)
                if filtered:
                    try:
                        os.write(master_fd, filtered)
                    except OSError:
                        break

            if master_fd in rfds:
                try:
                    data = os.read(master_fd, 4096)
                except OSError:
                    break
                if not data:
                    break
                try:
                    os.write(stdout_fd, data)
                except OSError:
                    break
    finally:
        try:
            termios.tcsetattr(stdin_fd, termios.TCSAFLUSH, old_attrs)
        except termios.error:
            pass

    _, status = os.waitpid(pid, 0)
    if os.WIFEXITED(status):
        return os.WEXITSTATUS(status)
    return 1


if __name__ == "__main__":
    sys.exit(main())
@KyungmoKu commented on GitHub (Feb 9, 2026): Had the same issue - Windows Terminal SSH into Ubuntu. This worked for me: a small Python script that wraps opencode and filters out the garbage from stdin before it reaches the TUI. - Save below file as `osc-filter` (no .py) somewhere in your PATH, `chmod +x osc-filter`, then run `osc-filter opencode` instead of `opencode`. - You can alias it in your bashrc for convenience: `alias oc='osc-filter opencode'` The proper fix probably belongs in opentui's input parser - it doesn't handle OSC sequences at all right now. <details> <summary>osc-filter</summary> ```python #!/usr/bin/env python3 """PTY wrapper that filters OSC escape sequences from stdin. Windows Terminal sends unsolicited OSC queries (10/11/4) over SSH. The TUI's input parser has no OSC response handler, so the responses appear as garbage text like "eeee/eeee/eeee" in the input field. This wrapper runs the child command in a real PTY and strips OSC sequences from stdin before forwarding them to the child. Usage: osc-filter opencode [args...] OSC sequence format: ESC ] ... BEL (\x1b] ... \x07) ESC ] ... ESC \ (\x1b] ... \x1b\\) """ import fcntl import os import pty import select import signal import sys import termios ESC = 0x1B BEL = 0x07 BACKSLASH = ord("\\") # Stateful: tracks whether we're inside a partial OSC sequence that was # split across read() boundaries (e.g., SSH delivers the response in chunks). _in_osc = False def filter_osc(buf: bytes) -> bytes: """Remove OSC sequences from a byte buffer. Handles split reads: if an OSC sequence spans two read() calls, the second half is suppressed until the terminator is found. """ global _in_osc out = bytearray() i = 0 n = len(buf) # Continuing a partial OSC from the previous read if _in_osc: while i < n: if buf[i] == BEL: i += 1 _in_osc = False break if buf[i] == ESC and i + 1 < n and buf[i + 1] == BACKSLASH: i += 2 _in_osc = False break i += 1 if _in_osc: return b"" while i < n: if buf[i] == ESC and i + 1 < n and buf[i + 1] == ord("]"): i += 2 while i < n: if buf[i] == BEL: i += 1 break if buf[i] == ESC and i + 1 < n and buf[i + 1] == BACKSLASH: i += 2 break i += 1 else: _in_osc = True else: out.append(buf[i]) i += 1 return bytes(out) def copy_window_size(from_fd: int, to_fd: int) -> None: try: size = fcntl.ioctl(from_fd, termios.TIOCGWINSZ, b"\x00" * 8) fcntl.ioctl(to_fd, termios.TIOCSWINSZ, size) except OSError: pass def main() -> int: if len(sys.argv) < 2: print(f"Usage: {sys.argv[0]} <command> [args...]", file=sys.stderr) return 1 if not os.isatty(sys.stdin.fileno()): os.execvp(sys.argv[1], sys.argv[1:]) pid, master_fd = pty.fork() if pid == 0: os.execvp(sys.argv[1], sys.argv[1:]) stdin_fd = sys.stdin.fileno() stdout_fd = sys.stdout.fileno() old_attrs = termios.tcgetattr(stdin_fd) try: new_attrs = termios.tcgetattr(stdin_fd) new_attrs[0] = 0 new_attrs[1] = 0 new_attrs[2] &= ~(termios.CSIZE | termios.PARENB) new_attrs[2] |= termios.CS8 new_attrs[3] = 0 new_attrs[6][termios.VMIN] = 1 new_attrs[6][termios.VTIME] = 0 termios.tcsetattr(stdin_fd, termios.TCSAFLUSH, new_attrs) except termios.error: pass copy_window_size(stdin_fd, master_fd) def handle_sigwinch(signum, frame): copy_window_size(stdin_fd, master_fd) try: os.kill(pid, signal.SIGWINCH) except OSError: pass signal.signal(signal.SIGWINCH, handle_sigwinch) try: while True: try: rfds, _, _ = select.select([stdin_fd, master_fd], [], []) except InterruptedError: continue if stdin_fd in rfds: try: data = os.read(stdin_fd, 4096) except OSError: break if not data: break filtered = filter_osc(data) if filtered: try: os.write(master_fd, filtered) except OSError: break if master_fd in rfds: try: data = os.read(master_fd, 4096) except OSError: break if not data: break try: os.write(stdout_fd, data) except OSError: break finally: try: termios.tcsetattr(stdin_fd, termios.TCSAFLUSH, old_attrs) except termios.error: pass _, status = os.waitpid(pid, 0) if os.WIFEXITED(status): return os.WEXITSTATUS(status) return 1 if __name__ == "__main__": sys.exit(main()) ``` </details>
Author
Owner

@gbpdt commented on GitHub (Feb 13, 2026):

Had the same issue - Windows Terminal SSH into Ubuntu.

This worked for me: a small Python script that wraps opencode and filters out the garbage from stdin before it reaches the TUI.

Thanks for posting this. I've given this workaround a try and it seems to work for me. I'd be very keen to see this integrated into the TUI code itself.

@gbpdt commented on GitHub (Feb 13, 2026): > Had the same issue - Windows Terminal SSH into Ubuntu. > > This worked for me: a small Python script that wraps opencode and filters out the garbage from stdin before it reaches the TUI. Thanks for posting this. I've given this workaround a try and it seems to work for me. I'd be very keen to see this integrated into the TUI code itself.
Author
Owner

@llc1123 commented on GitHub (Feb 13, 2026):

It is fixed upstream. Waiting for a new release. https://github.com/PowerShell/openssh-portable/pull/806

@llc1123 commented on GitHub (Feb 13, 2026): It is fixed upstream. Waiting for a new release. https://github.com/PowerShell/openssh-portable/pull/806
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#3856