Skip to content

[TUI] Windows: crash on window resize - segfault via Bun FFI into OpenTUI native lib; NOT fixed by Bun 1.4.2 (reproducible ConPTY harness + 2 decoded stacks) #54258

Closed as not planned

Description

@setnewgod

UPDATE — Bun 1.4.2 does not fix it (built opencode from source to test)

I rebuilt opencode 1.18.35 from source with Bun 1.4.2 (bun build --compile, windows x64) and ran the identical harness: still crashes, same class, new stack:

Both stacks die on the FFI call boundary into OpenTUI's native library (@opentui/core Zig dll; the JS layer calls e.g. symbols.resizeRenderer(renderer, width, height) via bun:ffi) from an async resize microtask. Reading: a resize lifecycle race in the OpenTUI native renderer (resize/damage calls racing renderer teardown/rebuild), surfacing through Bun's FFI call host on both Bun versions. A Bun bump alone is therefore not a sufficient fix — the resize lifecycle in OpenTUI's native renderer needs serialization/guarding (or Bun's FFI call host hardened).

Environment

  • OpenCode: 1.18.35 (npm opencode-ai, standalone opencode.exe)
  • Affected builds: both opencode-windows-x64 and opencode-windows-x64-baseline (tested separately, same crash)
  • Embedded Bun: 1.3.14 (0d9b296a) — crash report confirms; latest Bun is 1.4.2
  • OS: Windows x64 (Windows v.win10_cu per bun.report), CPU: sse42 avx avx2
  • Terminal: reproduces under ConPTY (Windows Terminal / conhost); reported by many users across all terminals

What happens

Resizing the terminal window (drag-resize, maximize/shrink) frequently kills the whole TUI process — the window just closes. This is the long-running family reported in #3328, #7539, #7943, #18614, #28284 ("在放大和缩小窗口会闪退"), #25232, #34231 — all stale-closed without a durable fix.

Unlike the earlier reports (which had no stack), I captured the actual crash output by running the TUI inside a ConPTY harness (pywinpty) and driving programmatic resizes:

Bun v1.3.14 (0d9b296a) Windows x64 (baseline)
CPU: sse42 avx avx2
RSS: 1.10GB | Peak: 1.19GB
panic (main thread): Segmentation fault at address 0x271FFE817FC
oh no: Bun has crashed. This indicates a bug in Bun, not your code.
https://bun.report/1.3.14/ea10d9b296m5GugooCC8B.bdff9febcfe77f9c-00000000.dll2k3pC_is33sB___A2inBpg0/C

bun.report decodes the stack to:

  • Zig::JSFFIFunction::trampoline at JSFFIFunction.cpp:166
  • Segmentation fault; exit code 3

i.e. the crash is in Bun's FFI-callback trampoline — the OpenTUI renderer calls Win32 console APIs through bun:ffi, and a resize event storm hits the trampoline in a bad state. The modern (opencode-windows-x64) build crashes with the same signature.

Characterization (synthetic ConPTY resize matrix, 1.18.35)

resize pattern result
normal sizes only (28×90 … 50×200), slow (0.25s) no crash (3 rounds)
normal sizes only, rapid (60 × 0.03s) no crash
shrink to tiny (2×2/3×4), slow, no grow-back no crash
tiny ↔ normal round-trip only (3 sizes) no crash
mixed sequence incl. tiny sizes with quick swings (14 sizes, 0.12s) crash within round 1, exit 3, both builds

Real drag-resizing generates exactly the last pattern (dense event stream with large width/height swings, sometimes passing through very small sizes), which matches reports that maximize/shrink/drag "sometimes" crashes.

Repro

Requirements: Python 3, pip install pywinpty.

# Repro for: anomalyco/opencode TUI crash on window resize (Windows)
# Crash: Bun 1.3.14 standalone (opencode 1.18.35) — segfault in
#        Zig::JSFFIFunction::trampoline (JSFFIFunction.cpp:166), exit code 3.
# Usage: pip install pywinpty && python tui_resize_repro.py
# Expected: process dies within round 1 with "oh no: Bun has crashed".
# Sanity:   `opencode --mini` under the same sequence survives (see issue notes).

import os, time, threading

from winpty import PtyProcess

EXE = r"D:\DEV\nvm\nvm4w\nodejs\node_modules\opencode-ai\bin\opencode.exe"
CWD = r"F:\WIN_TEMP\opencode\tui-test"
os.makedirs(CWD, exist_ok=True)

buf = []
eof = threading.Event()

proc = PtyProcess.spawn(EXE, cwd=CWD, dimensions=(35, 120))

def reader():
    while True:
        try:
            data = proc.read(4096)
            if not data:
                eof.set()
                break
            buf.append(data)
        except Exception:
            eof.set()
            break

threading.Thread(target=reader, daemon=True).start()

def alive():
    return proc.isalive()

time.sleep(8)
print(f"[boot] alive={alive()} out={sum(len(x) for x in buf)} winsize={proc.getwinsize()}")

# (rows, cols) — 从正常尺寸逐步缩到极小再放大,模拟拖拽拉伸
sizes = [(35,120),(30,100),(25,80),(20,60),(15,40),(8,20),(5,10),(3,4),(2,2),
         (15,40),(25,80),(35,120),(50,200),(35,120)]
crashed = False
try:
    for rnd in range(5):
        for (r, c) in sizes:
            if not alive() or eof.is_set():
                crashed = True
                break
            try:
                proc.setwinsize(r, c)
            except Exception as e:
                print(f"[resize {r}x{c}] error: {e}")
            time.sleep(0.12)
        print(f"[round {rnd+1}] alive={alive()}")
        if crashed:
            break
        time.sleep(0.4)

    if not crashed:
        print("[rapid] 60 rapid resizes (drag simulation)")
        for i in range(60):
            if not alive() or eof.is_set():
                crashed = True
                break
            r = 10 + (i * 7) % 40
            c = 30 + (i * 13) % 170
            try:
                proc.setwinsize(r, c)
            except Exception:
                pass
            time.sleep(0.03)

    time.sleep(2)
finally:
    status = proc.exitstatus if not alive() else "n/a(still-alive)"
    print(f"[final] alive={alive()} crashed={crashed} exitstatus={status}")
    text = "".join(buf)
    markers = ["panic", "Panic", "goroutine", "Bun has crashed", "oh noes",
               "Segmentation", "ACCESS_VIOLATION", "Assertion", "index out of range",
               "unreachable", "Fatal", "MAXIMUM_UPDATE", "unhandled"]
    hits = [m for m in markers if m in text]
    print(f"[markers] {hits}")
    print("=== LAST 3000 CHARS ===")
    print(text[-3000:])
    try:
        proc.terminate()
    except Exception:
        pass

Expected when it crashes: panic(main thread): Segmentation fault, oh no: Bun has crashed, exit code 3, plus a bun.report URL.

Expected

The TUI survives arbitrary legal resize sequences (including tiny intermediate sizes during drag).

Requested fix

The crash survives a Bun bump (1.3.14 → 1.4.2, verified by source build above), so the fix likely lives in OpenTUI's native renderer resize lifecycle (serialize resize/damage against renderer teardown; the harness gives a deterministic pass/fail) — or in Bun's FFI call host (FFICallHost.cpp / JSFFIFunction.cpp, both stacks decoded above). Happy to test any canary/dev build against the harness.

Workarounds that currently work (verified locally on 1.18.35)

  • opencode --mini (minimal interface, no OpenTUI renderer): survives the exact killing sequence, zero panics
  • opencode web (browser UI)

Activity

  1. opencode-agent commented on Oct 10, 2026

    @opencode-agent
    Contributor

    Thanks for the very thorough report, including the ConPTY harness, the decoded stacks and the resize matrix.

    This report is about OpenCode v1 (1.18.35, opencode-ai). v1 now only gets fixes for critical bugs such as data loss, security problems, or breakage with no workaround. Since opencode --mini and opencode web both avoid the crash, this is unlikely to be fixed in v1.

    Please try v2 (@opencode/cli, the opencode command). Its TUI still uses OpenTUI, and a similar resize crash has already been reported for v2 on Windows:

    If your harness also crashes v2, please post the result and the bun.report URL on #51167, or reply here. That would help a lot in tracking it down. Related earlier reports, for reference: #18614, #28284, #31144.

    If you think this is critical for v1, please reply and explain why.

  2. opencode-agent commented on Oct 11, 2026

    @opencode-agent
    Contributor

    Closing this as not planned. It's a v1 (1.18.35) issue, v1 only gets fixes for critical bugs, and opencode --mini and opencode web both avoid the crash. There hasn't been a reply saying it's critical or that it also happens on v2.

    Thanks again for the harness and the decoded stacks. If you run the harness against v2 and it still crashes, please post the result and the bun.report URL on #51167, which tracks the same resize crash on Windows in v2.

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions