Hi Nicholas, Thank you for chiming in — I certainly didn't expect to draw the attention of tmux's author on the ncurses mailing list!
You're right, and I apologize for the noise. I should have read the 3.6 release notes more carefully before going down this rabbit hole. The note in https://github.com/tmux/tmux/issues/4699 describes exactly the symptom I was seeing. For what it's worth, the investigation did eventually converge on the same conclusion — the imsg library protocol change between 3.5a and 3.6b makes old servers unable to receive fds from new clients. I just took the scenic route getting there, and incorrectly blamed ncurses along the way. One thought: would it be feasible for the tmux client to detect a server version or protocol mismatch and print a more specific error message (e.g., "server was started by an older tmux version, please run 'tmux kill-server' and retry")? The current "open terminal failed: not a terminal" doesn't hint at the actual cause, which is probably why both I and the earlier 2026-02 reporter ended up looking in the wrong places. In any case, thank you for the clarification, and sorry again for the misdirected report. Best, Liam > 2026年5月27日 17:15,Nicholas Marriott <[email protected]> 写道: > > This was already mentioned in the tmux 3.6 release notes, see the second > paragraph here: > > https://github.com/tmux/tmux/issues/4699 > > > > > > On Wed, 27 May 2026, 08:27 Liam Huang, <[email protected] > <mailto:[email protected]>> wrote: >> Hi Thomas, >> >> Thank you for your reply and pointers. I'm writing back with a correction: >> after further investigation, I've determined that this is NOT an ncurses >> bug. I apologize for the noise. >> >> >> CORRECTION >> >> The "open terminal failed: not a terminal" error is caused by a tmux >> client/server version mismatch after a Homebrew upgrade, not by ncurses >> 6.6's setupterm(). ncurses is innocent here. >> >> >> WHAT WENT WRONG WITH MY ORIGINAL ANALYSIS >> >> My isolation test used LD_LIBRARY_PATH to swap ncurses versions at >> runtime. I believed this showed ncurses 6.6 as the regression boundary. >> However, the brew tmux binary has a hardcoded DT_RPATH (not DT_RUNPATH) >> pointing to its ncurses directory. DT_RPATH takes priority over >> LD_LIBRARY_PATH, so all three test groups were actually running with >> ncurses 6.6 — the test was invalid. >> >> The real variable I failed to control was the tmux server version. Before >> `brew upgrade`, an older tmux (3.5a) server was still running. After the >> upgrade, the new 3.6b client connected to the old server, triggering the >> failure. >> >> >> ACTUAL ROOT CAUSE >> >> tmux 3.6b upgraded its bundled OpenBSD imsg IPC library. The struct >> imsg_hdr layout changed in an ABI-incompatible way: >> >> Old (3.5a): type(u32) + len(u16) + flags(u16) + peerid(u32) + pid(u32) >> New (3.6b): type(u32) + len(u32) + peerid(u32) + pid(u32) >> >> The old library marks "message carries an fd" with flags & IMSGF_HASFD >> (bit 0 of the flags field). The new library uses len & 0x80000000 >> (bit 31 of the widened len field), with the flags field removed entirely. >> >> When the new client sends MSG_IDENTIFY_STDIN (carrying the terminal fd), >> the old server misparses the header: it reads len=0x0010, flags=0x8000, >> and since 0x8000 & IMSGF_HASFD(0x0001) == 0, it never dequeues the fd. >> The client's terminal fd is lost, isatty() fails on a stale fd, and tmux >> reports "not a terminal". >> >> I confirmed this with strace (ioctl(TCGETS) returns ENOTTY on the >> received fd) and ncurses trace (setupterm() is never called — the failure >> occurs before ncurses is involved). >> >> >> CLARIFICATIONS ON YOUR REPLY >> >> A few small points, respectfully: >> >> 1. "Homebrew isn't Ubuntu - it's MacOS" >> >> Homebrew has officially supported Linux since 2019 (the former >> Linuxbrew project merged into mainline). It is widely used on Linux >> servers and in CI environments. My setup — Homebrew on Ubuntu — is a >> supported configuration, not an exotic one. >> >> 2. "This isn't the Ubuntu bug-reporting system" >> >> Understood. I was not reporting an Ubuntu/Debian packaging issue. I was >> reporting what I believed to be a regression in ncurses 6.6 itself, >> tested with a from-source build using the same configure flags. As it >> turns out, the bug was not in ncurses at all, so this is moot. >> >> 3. The 2026-02 report you referenced (bug-ncurses/2026-02/msg00025) >> >> That earlier report may or may not be the same issue. If the reporter >> was also hitting a tmux version mismatch after an upgrade, it would >> explain why no reproducer was provided — the problem disappears once >> the old server is killed. It might be worth asking them whether they >> had recently upgraded tmux. >> >> >> SUMMARY >> >> - ncurses 6.6 setupterm() works correctly. I verified this with both a >> minimal C reproducer (SCM_RIGHTS fd passing + setupterm()) and ncurses >> trace output. >> - The bug is in tmux's cross-version compatibility (or lack thereof) >> after an imsg library ABI change. >> - I've updated the Homebrew issue (homebrew-core#284568) with the >> correction. >> >> ONE QUESTION >> >> Since the root cause is in tmux (the imsg library ABI change breaks >> cross-version client/server compatibility without bumping PROTOCOL_VERSION), >> would you recommend I report this to the tmux project? If so, could you >> suggest the appropriate channel — is it the GitHub issue tracker at >> https://github.com/tmux/tmux/issues, or the tmux-users mailing list? >> >> Again, I apologize for the incorrect report and for taking your time. >> Thank you for maintaining ncurses. >> >> Best regards, >> Liam >> >> >>> 2026年5月25日 22:42,Thomas Dickey <[email protected] >>> <mailto:[email protected]>> 写道: >>> >>> On Mon, May 25, 2026 at 10:15:16AM +0800, Liam Huang wrote: >>>> Hi, >>>> >>>> I've found a regression in ncurses 6.6 (patchdate 20251230) where >>>> setupterm() fails on valid Linux pseudo-terminals, causing tmux's >>>> tty_init() to fail and "open terminal failed: not a terminal" on attach. >>> >>> This sounds like a bug which was reported in tmux without providing a >>> way to reproduce it - >>> >>> https://lists.gnu.org/archive/html/bug-ncurses/2026-02/msg00025.html >>> >>>> The issue does NOT occur with ncurses 6.5 (patchdate 20240427) or >>>> the Ubuntu system ncurses 6.4. >>> >>> This isn't the Ubuntu bug-reporting system. Since Ubuntu only >>> mirrors ncurses updates from Debian, the place to report Ubuntu issues >>> is in Debian. >>> >>>> >>>> ENVIRONMENT >>>> >>>> OS: Ubuntu 24.04.4 LTS >>>> Kernel: 6.8.0-107-generic x86_64 >>>> tmux: 3.6b (Homebrew) >>> >>> Homebrew isn't Ubuntu - it's MacOS. >>> >>>> ncurses: 6.6.20251230 (Homebrew, wide-char build) >>>> 6.5.20240427 (built from source, same configure flags) >>>> 6.4+20240113 (Ubuntu system /lib/x86_64-linux-gnu) >>> >>> That's 3 versions of ncurses, one self-built, and (Homebrew isn't >>> far from that, either). If you're going to do all of that, you >>> should be able to configure ncurses for traces and provide that information. >>> >>>> REPRODUCTION >>>> >>>> # Using tmux 3.6b linked against ncurses 6.6: >>>> $ tmux new -d -s test >>>> $ tmux attach -t test >>>> open terminal failed: not a terminal >>>> >>>> This happens in: >>>> - Interactive SSH sessions (ssh user@host, then tmux a) >>>> - VS Code Remote integrated terminal >>>> - PTYs created by script(1) >>>> >>>> The ONLY working case is ssh -t host "tmux a -t test" (forced PTY >>>> with terminal size forwarding from the SSH client). >>>> >>>> >>>> ISOLATION >>>> >>>> Same tmux 3.6b binary, different ncurses library at runtime: >>>> >>>> | ncurses | method | result | >>>> |-------------------|-------------------------------------|---------| >>>> | 6.6.20251230 | default brew linkage | FAIL | >>>> | 6.5.20240427 | LD_LIBRARY_PATH=.../ncurses-6.5/lib | OK | >>>> | 6.4 (system) | LD_LIBRARY_PATH=/lib/x86_64-linux-gnu | OK | >>>> >>>> Commands to reproduce the isolation test: >>>> >>>> # FAIL with 6.6 >>>> script -qc 'tmux -S /tmp/t66 new -d -s x && \ >>>> tmux -S /tmp/t66 a -t x' /dev/null >>>> >>>> # OK with 6.5 >>>> script -qc 'LD_LIBRARY_PATH=/path/to/ncurses-6.5/lib \ >>>> tmux -S /tmp/t65 new -d -s x && \ >>>> LD_LIBRARY_PATH=/path/to/ncurses-6.5/lib \ >>>> tmux -S /tmp/t65 a -t x' /dev/null >>>> >>>> >>>> MECHANISM >>>> >>>> tmux's server receives the client's PTY fd via SCM_RIGHTS, then >>>> calls tty_init() -> setupterm(). With ncurses 6.6, setupterm() >>>> apparently rejects the terminal, causing tty_init() to return -1. >>> >>> "apparently" can be used with lots of supporting data (actual >>> system calls used, along with their results). >>> >>>> This leaves the CLIENT_TERMINAL flag unset, and server_client_open() >>>> returns "not a terminal". >>>> >>>> strace shows the PTY is valid — TCGETS succeeds, the fd resolves >>>> to /dev/pts/N, and permissions are correct (crw--w---- owner:tty). >>> >>> I don't see a trace (and in any case, this report deals with a large system, >>> in which things can be misconfigured) >>> >>>> The tmux source code for this path is identical between 3.4 and >>>> 3.6b, confirming the behavioral change is in ncurses, not tmux. >>>> >>>> >>>> BUILD FLAGS (both 6.5 and 6.6 built with): >>>> >>>> ./configure --prefix=... --with-shared --with-widec \ >>>> --enable-pc-files --enable-sigwinch --enable-ext-colors \ >>>> --without-debug --without-ada >>>> >>>> >>>> I haven't bisected the exact ncurses patch between 6.5 and 6.6 yet. >>>> Happy to test patches or provide additional traces if helpful. >>> >>> That's about 18 months of changes :-) >>> >>> -- >>> Thomas E. Dickey <[email protected] >>> <mailto:[email protected]>> >>> https://invisible-island.net <https://invisible-island.net/>
