---
title: "What's new in 2.9.23"
description: "The daemon keeps terminal streams when a tmux probe is slow, costs less under heavy output, and works with more stock tmux servers."
canonical: https://tmux-ide.com/docs/release-2-9-23
last_updated: 2026-11-13T19:20:49.000Z
---
# tmux-ide 2.9.23

> The daemon keeps terminal streams when a tmux probe is slow, costs less under heavy output, and works with more stock tmux servers.

`2.9.23` is a CLI/daemon release. It fixes dropped terminal connections, makes
the daemon cheaper under heavy output and at rest, and fixes five problems on
tmux servers that are not tmux-ide's own. It adds no commands and needs no Mac
app update.

## Terminals no longer drop while the daemon stays up

In 2.9.22 the daemon could close every terminal stream of a tmux server while
the daemon itself kept running. The Mac app then lost its terminals.

The cause: each time a client listed the tmux servers, the daemon asked every
server a question with a 500 ms limit. A slow answer, or a `tmux` that could not
be started, counted as "server gone", so the daemon dropped its connection to
that server, closed its streams and built a new connection on the next list. The
Mac app lists servers whenever it reads the catalog, opens a session or creates
one, so it was hit far more often than the TUI, which lists only when it
reconnects.

The reproduction, on a separate daemon with tmux answering in 0.7 s, one list
request and one open stream:

|                | 2.9.22      | 2.9.23      |
| -------------- | ----------- | ----------- |
| Streams closed | 1           | 0           |
| Connection     | rebuilt     | kept        |
| List answered  | after 5.7 s | after 0.5 s |

A `tmux` that cannot be started because the process limit is reached gave the
same drop in 2.9.22 and gives none in 2.9.23. A busy tmux server, many list
requests at once and plain CPU load did not reproduce the drop. Which of the two
causes hit a given machine can't be told from 2.9.22's log; 2.9.23 logs it.

What changed:

* Read requests can no longer retire or replace a tmux server connection. They
  wait at most 1 s for work in flight, then answer with what is known.
* A background check every 2 s watches the servers. It starts no `tmux` process
  while their sockets and processes are unchanged.
* The daemon retires a connection only on evidence that the server changed: its
  socket was replaced, its process is gone, or another process answers. A second
  probe must confirm it.
* A connection that fails to build is logged and retried, from 500 ms up to
  every 30 s.

One case is left open: when a tmux server has died, another process has taken
its process id, and its old socket file is still there, the server is listed as
online until one of those changes.

## A stop that ends

After SIGTERM or SIGINT the daemon now stops within a limit. If its teardown
makes no progress for 10 s, or takes 25 s in all, it logs the steps still open,
removes `daemon.json` and exits with status 1. Two waits on the stop path that
had no limit now have one. A restart in place whose stop hangs exits the same
way.

We have not found why a daemon sometimes did not stop after its tmux server was
replaced. It did not happen in 74 local runs. The limit and the new log line are
there to end the hang and to name the step if it happens again.

## More connection fixes

* Two daemons on one tmux server no longer remove each other's hooks. A
  daemon's hook install removed every tmux-ide daemon's hooks, and each gap lost
  `send-keys` and `capture-pane` observations and made clients re-read the
  terminal inventory. A daemon now removes only its own hooks and those no
  living daemon owns. A daemon older than 2.9.23 on the same server still
  removes everyone's.
* Terminal streams and the events socket have a heartbeat. The daemon pings
  every 10 s and drops a connection after 3 unanswered pings, so a half-open
  stream no longer looks like a frozen terminal.
* Every synchronous tmux call has a 5 s limit. One hung tmux call could freeze
  the whole daemon, including its signal handling.
* An absent or stale tmux server costs a file check or a connect, not a tmux
  process. A refused connection never retires a server connection, and on macOS
  touching the socket file no longer does either.
* A daemon restart in place keeps its port.
* A terminal inventory read that was overtaken twice by changes answered 503.
  It now keeps reading. This was one cause of a click on an agent that had just
  appeared doing nothing.
* With `--json`, all log records go to stderr, so stdout stays one JSON
  document.
* `tmux-ide doctor` adds a "tmux server" row with the socket the daemon uses.
  It warns when this shell's tmux (`TMUX_TMPDIR`, or a tmux started with `-S`
  or `-L`) means another server. See
  [Troubleshooting](/docs/troubleshooting).

## A cheaper daemon

Measured on a separate daemon, with runs of the old and the new build
alternated.

Heavy output in a viewed pane no longer slows tmux itself. The daemon committed
after every write, which made it the bottleneck for tmux. It now skips a commit
while more output is queued. With a 2 MB flood and one viewer:

|                                   | 2.9.22  | 2.9.23 |
| --------------------------------- | ------- | ------ |
| Daemon CPU time                   | 2.16 s  | 1.05 s |
| tmux's time to produce the output | 1557 ms | 616 ms |
| Frames delivered to the viewer    | 11      | 14     |

A pane nobody has watched for 10 s is parked. tmux pauses its output to the
daemon, and the daemon keeps the last frame. Agent status is not affected. The
cost: when you return to a parked pane, the last frame appears as fast as
before, and the current content follows after about 250 to 290 ms instead of
100 to 130 ms. On a heavily loaded machine it takes longer.

The daemon uses less memory at rest. Its process footprint fell from
327–333 MiB to 283–288 MiB.

The daemon starts fewer tmux processes while idle. The check of its healthy
hooks runs every 5 minutes instead of every 30 s: with three tmux servers, 5.8
tmux processes a minute become 0.6. The fleet poll slows from every 500 ms to
every 1 s after ten unchanged reads, which halves its tmux processes while
idle. A CPU saving from the fleet poll was not measured.

## tmux servers that are not tmux-ide's own

Five fixes for a server started by Homebrew's or another stock tmux (3.7c and
3.8 were tested):

* On tmux 3.8, a window with `pane-border-status` set to `top-floating` or
  `bottom-floating` can be opened. Before, its session had no pane to attach
  to.
* Opening a session on an agent works. Before, it answered 503.
* Browser panes and the Markdown reader work. Before, both answered 503. The
  Mac app was not run against a stock server for this fix.
* When the tmux socket cannot be read, the workspace catalog answers 503
  `tmux-unavailable` instead of an empty session list.
* The daemon starts on a tmux socket whose path is too long once `/tmp` is
  written as `/private/tmp`. Before, it exited at startup.

Panes whose content holds tab characters still do not load on a stock server.

## Also in 2.9.23

* Clients keep re-reading a terminal inventory the daemon refuses: after 250 ms,
  500 ms, then every 1 s for up to 15 s. After that the client reconnects, with
  1, 2, 4 and up to 30 s between attempts. Before, it stopped after two quick
  retries and kept showing a stale terminal.
* The daemon announces when a session's terminal inventory is readable again, so
  an idle client re-reads it.
* A terminal stream closes with code 1012 and reason `tmux-server-retired` when
  its tmux server is gone or replaced, and 1012 `daemon-restarting` when the
  daemon restarts in place. 1001 `daemon-shutdown` now means the daemon stops.
* The daemon announces on the events socket when a tmux server comes online or
  is retired.

## New log lines

The daemon log gains:

* `tmux-server-owners`: a connection kept after a failed probe, retired, built
  or failed to build, with the trigger, the probe's duration and its error.
* `daemon-lifecycle`: a restart in place with its trigger, and a slow or failed
  stop with its slowest steps. A stop that misses its limit logs
  `stop missed the signal deadline; exiting anyway`.
* `pane-stream`: a shutdown that closes streams, with the code, the reason and
  the number of streams.

## Updating

Update with `tmux-ide update` or rerun the installer. A running older daemon
restarts on 2.9.23 by itself; your tmux sessions keep running. Update remote
machines too: the fix is in the daemon on the machine that runs tmux.

Full list of changes:
[CHANGELOG.md](https://github.com/wavyrai/tmux-ide/blob/main/CHANGELOG.md).
