Skip to content

tui: Kitty graphics probe (Gi=31337) leaks into the shell prompt after exiting V2 #55641

Open

Description

@dersenok

Summary

After exiting the V2 TUI on Linux, a leftover escape-sequence fragment from OpenCode's Kitty-graphics capability probe is printed at the shell prompt. It is cosmetic, but it leaves stray bytes on the readline line, which can block Ctrl+D (EOF).

Environment

  • opencode version: 2.0.29
  • OS: Linux x86_64, kernel 7.2.10-x64v3-xanmod1 (Linux ... #0~20261011.g455a2bf SMP PREEMPT_DYNAMIC Sun Oct 11 16:57:09 UTC 2026 x86_64 GNU/Linux)
  • Terminal: qTerminal 2.1.0 (TERM=xterm-256color, COLORTERM=truecolor, TERM_PROGRAM unset)
  • Shell: /bin/bash
  • Install/channel: release (single binary at ~/.opencode/bin/opencode)
  • Active plugins: one TUI plugin, file:///home/<user>/.config/opencode/plugins/orca-opencode-status-tui/tui.js. No project-local config.
  • Multiplexer: none ($TMUX unset)

Reproduction

  1. Open qTerminal with bash.

  2. Run opencode (v2.0.29).

  3. Exit the TUI normally (Ctrl+C or /exit).

  4. Observe leftover bytes on the shell prompt line:

    [user@host ~/AI_DEV]$ oc
    +q4d73ppppppGi=31337,s=1,v=1,a=q,t=d,f=24;AAAA
    

Reproducible on every exit.

Expected Behavior

The shell prompt is clean after OpenCode exits; terminal capability-probe replies are fully consumed before the terminal is restored to cooked mode.

Actual Behavior

A leftover fragment from the Kitty-graphics capability probe is printed at the prompt:

+..+q4d73ppppppGi=31337,s=1,v=1,a=q,t=d,f=24;AAAA

Additional Context

The leaked fragment matches, byte for byte, the Kitty-graphics capability probe OpenCode sends at startup. It is present in the shipped binary:

$ grep -a -o '.\{40\}a=q,t=d,f=24.\{20\}' ~/.opencode/bin/opencode
...\x1b_Gi=31337,s=1,v=1,a=q,t=d,f=24;AAAA\x1b\\...

So Gi=31337,s=1,v=1,a=q,t=d,f=24;AAAA is OpenCode's own startup probe (a=q = query, i=31337 = magic image id, AAAA = base64 of a 1x1 black pixel), not a terminal reply. The +q4d73pppppp prefix looks like a fragment of an adjacent probe/reply (the ppppp garble matches #15084).

This appears to be the POSIX half of the terminal-teardown bug class that was fixed for V1 in #17081 (stale stdin bytes leak to shell after exit on POSIX). Per that issue, opentui's teardown removed the stdin listener and disabled raw mode but never flushed the tty input buffer; the V1 fix added tcflush(STDIN_FILENO, TCIFLUSH). This looks like a V2 regression.

Related issues: #17081, #15084, #16547, #24475, and current V2 reports #55163 (raw mode left after /exit) and #55213.

Notes:

  • Not running under tmux, so allow-passthrough is not involved.
  • cli.json already sets theme.mode: "dark", so the OSC 10/11 theme-color probes are not the trigger; the leaked bytes are the graphics probe.
  • No public V2 setting exists to disable the Kitty-graphics capability probe.

Workarounds:

  • Ctrl+L clears the line; reset / stty sane recovers a jammed prompt.
  • Shell hook: precmd() { stty sane; } (zsh) or PROMPT_COMMAND='stty sane' (bash).

Activity

  1. opencode-agent commented on Oct 15, 2026

    @opencode-agent
    Contributor

    Thanks for the detailed report. I can reproduce this on 2.0.29 in qTerminal (TERM=xterm-256color, bash, no tmux). After exiting the TUI, …73ppppppGi=31337,s=1,v=1,a=q,t=d,f=24;AAAA is left on the screen. The recording and screenshot below show it, and the script below reproduces it under Xvfb.

    I think the cause is a bit different from #17081, though. A raw capture of the terminal output shows OpenCode sends its capability probes on the normal screen before it switches to the alternate screen:

    • an XTGETTCAP query (ESC P +q4d73 ESC \)
    • six DECRQM mode queries ending in $p
    • the Kitty graphics query (ESC _ Gi=31337…;AAAA ESC \)

    qTerminal doesn't understand DCS/APC sequences, so it draws their contents as text: +q4d73, then a p from each mode query, then Gi=31337…. That text is still on the normal screen when OpenCode exits.

    In my test this was only on the screen. Typing a command right after exiting worked normally, so the shell's input line held no stray bytes, and I couldn't reproduce the Ctrl+D problem. If Ctrl+D is still blocked for you, it would help to know what's on the line when you press it. For example, does echo hi run cleanly right after exiting?

    I couldn't run the latest dev (4397ee4) from source here. But the latest release is still 2.0.29, and the terminal library that sends these probes (@opentui 0.5.17) is the same version on dev, so it most likely still happens there.

    recording.mp4

    Screenshot

    Reproduction script (qterm-repro.sh)
    #!/bin/bash
    # Reproduces #55641: launch opencode inside qTerminal (under Xvfb), exit, screenshot the shell prompt.
    # Usage: OC_CMD="opencode" ./qterm-repro.sh   (needs qterminal, Xvfb, xdotool, imagemagick; ffmpeg optional)
    set -u
    OC_CMD=${OC_CMD:-opencode}
    OUT=${OUT:-/workspace/media}
    mkdir -p "$OUT" /workspace/repro/proj
    export DISPLAY=:99
    Xvfb :99 -screen 0 1280x800x24 >/dev/null 2>&1 & XV=$!
    sleep 2
    REC=""
    if command -v ffmpeg >/dev/null; then
      ffmpeg -loglevel error -y -f x11grab -video_size 1280x800 -framerate 10 -i :99 -pix_fmt yuv420p "$OUT/qterminal-repro.mp4" & REC=$!
    fi
    cd /workspace/repro/proj
    env -u TERM_PROGRAM -u TMUX qterminal >/dev/null 2>&1 & QT=$!
    sleep 4
    WID=$(xdotool search --pid $QT | tail -1)
    xdotool windowactivate --sync "$WID" 2>/dev/null; xdotool windowsize "$WID" 1200 760 windowmove "$WID" 0 0
    sleep 1
    xdotool type --delay 50 "echo TERM=\$TERM; $OC_CMD"; xdotool key Return
    sleep 12
    import -window root "$OUT/qterminal-in-tui.png"
    xdotool key ctrl+c; sleep 1; xdotool key ctrl+c
    sleep 4
    xdotool type --delay 50 "echo READLINE_CLEAN"; xdotool key Return; sleep 2
    import -window root "$OUT/qterminal-after-exit.png"
    sleep 1
    [ -n "$REC" ] && kill -INT $REC && sleep 2
    kill $QT $XV 2>/dev/null

    Run it: bash qterm-repro.sh

  2. dersenok commented on Oct 15, 2026

    @dersenok
    Author

    Reporter here — thanks for the detailed repro. Confirming from my side, with one difference from your run:

    Ctrl+D does not work right after exit, and unlike your test there is a stray character sitting on the readline line. Pressing Enter (or Ctrl+L) clears it, and then Ctrl+D works again. So on my machine both halves are present:

    1. the DCS/APC probe text drawn on the normal screen (exactly as you describe: +q4d73 + six ps + Gi=31337,…), and
    2. at least one stray byte left in the tty input buffer — the Stale stdin bytes leak to shell after exit on POSIX (space on prompt, blocks ctrl+d) #17081 symptom — which sits on the line and blocks EOF.

    So it looks like V2 may have regressed the #17081 tcflush fix while the screen-scrape half (probes emitted before entering the alt screen) is separate.

    Env: qTerminal 2.1.0, TERM=xterm-256color, COLORTERM=truecolor, /bin/bash, no tmux, opencode 2.0.29.

    Happy to run a targeted diagnostic if useful — e.g. dump the readline line as octal right after exit, before touching the keyboard, to identify which byte leaks into the input buffer. Just tell me what you'd like captured.

  3. Finn763 commented on Oct 21, 2026

    @Finn763
    Contributor

    I'd like to work on this — the Kitty graphics probe leaves its response fragment unread at exit; I'll clear it in the TUI shutdown path.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions