Releases: tmux-python/libtmux
Release list
v0.62.0 - Locate yourself, resolve linked windows
Highlights
libtmux 0.62.0 teaches libtmux objects to locate themselves and to resolve shared windows the way tmux does. Code running inside tmux can now find the object it's running in, a window can name every session that holds it, and point lookups follow tmux's own choice of session and index instead of an arbitrary one. One breaking change lands too — query errors now belong to the LibTmuxException hierarchy (see below).
Find where you're running — from_env()
Most libtmux code starts from a handle you already hold. But a script in a split, a tmux hook, a test harness, or an agent is running inside a pane and holds nothing. tmux already wrote the answer into the pane's environment, and each level of the hierarchy now reads it back:
from libtmux import Pane
pane = Pane.from_env() # the pane this code is running in
window = pane.window # …and you're back on the hierarchy from there
session = window.sessionServer.from_env(), Session.from_env(), and Window.from_env() do the same for the rest of the tree. Each also accepts an environment mapping in place of os.environ, so code that locates itself stays testable outside a pane. Outside tmux — or with an environment that doesn't parse — the family raises libtmux.exc.NotInsideTmux rather than guessing.
The answer stays true as tmux moves things around: it survives a move-window, and for a window linked into several sessions it names the session tmux itself would act on. See the Locating yourself guide for the whole story.
Ask a window which sessions hold it — Window.linked_sessions
A window can be linked into more than one session at once. Window.linked_sessions lists every Session from which a window is reachable — a typical window returns one, a linked or grouped window returns several, with each session appearing once even when it holds the window at multiple indexes. Use it when you need every holder; Window.session still follows the single session_id recorded on that instance.
Linked windows resolve the way tmux does
Window.from_window_id(), Pane.from_pane_id(), Window.refresh(), and Pane.refresh() used to pick one holding session by accident — whichever sorted last by name — so their answer could change when you renamed a session. They now let tmux resolve the object, reporting the session tmux itself would act on, and give Window.window_index the index tmux would choose for a window linked twice. For the ordinary single-session window, nothing changes; the lookups also got cheaper. See winlinks.
Lookups that say what happened
QueryList.get() now names both the lookup and its outcome. A missing pane_id="%99" raises ObjectDoesNotExist with No objects found: pane_id='%99'; two matches for pane_id="%0" raise MultipleObjectsReturned with Multiple objects returned (2): pane_id='%0'.
⚠️ Breaking change: query errors join the LibTmuxException hierarchy
libtmux.exc.ObjectDoesNotExist and libtmux.exc.MultipleObjectsReturned now subclass libtmux.exc.LibTmuxException. Because TmuxObjectDoesNotExist inherits from ObjectDoesNotExist, it now falls under LibTmuxException too.
If you catch both families, order the handlers from most specific to most general so the broad clause no longer intercepts lookup outcomes:
from libtmux import exc
try:
pane = server.panes.get(pane_id="%0")
except exc.ObjectDoesNotExist: # specific first
pane = None
except exc.LibTmuxException: # general last
pane = NoneBlanket retry policies for LibTmuxException should now exclude ObjectDoesNotExist and MultipleObjectsReturned — they report deterministic missing or ambiguous lookups, not transient tmux failures. See the migration notes for a retry predicate example.
What's Changed
- Correct AGENTS.md fixture guidance by @tony in #708
- fix(Pane,Window): Resolve point lookups through tmux by @tony in #713
- feat(Server,Session,Window,Pane): Resolve the pane you run inside (from_env) by @tony in #714
- Resolve winlinks: the right window index, and lookups that say what happened by @tony in #718
Full Changelog: v0.61.0...v0.62.0
v0.61.0 - tmux 3.7a and 3.7b test fixes
libtmux 0.61.0 hardens support for the tmux 3.7 patch line. It fixes Pane.break_pane() naming broken-out windows libtmux instead of tmux's own default on tmux 3.7a/3.7b, and adds get_version_str() for reading the raw tmux version with its point-release suffix intact.
Highlights
- Fix —
break_pane()keeps tmux's default window name on 3.7a/3.7b (#699). Breaking a pane into a new window without an explicitwindow_nameleft it namedlibtmuxon tmux 3.7a/3.7b; it now keeps tmux's own default (typically the running command). Passingwindow_nameis unaffected. - New —
get_version_str()(#699).libtmux.common.get_version_str()returns the running tmux version verbatim, keeping the point-release suffix ("3.7a") thatget_version()strips for numeric comparison — useful for telling patch releases apart, e.g.3.7from3.7a. - tmux 3.7a/3.7b test compatibility (#698). The test suite now passes against tmux 3.7a and 3.7b, which CI also exercises.
What's Changed
- docs: lead topic pages with the concept, add docs voice guide by @tony in #696
- Fix window-name test for tmux 3.7a/3.7b and add them to CI by @tony in #698
- Fix break_pane forcing 'libtmux' window name on tmux 3.7a/3.7b by @tony in #699
Full Changelog: v0.60.0...v0.61.0
v0.60.0 - tmux 3.7 parity
libtmux 0.60.0 completes tmux 3.7 feature parity. Building on the 3.7 compatibility shipped in 0.59.0, it adds first-class floating panes via Pane.new_pane() and Window.new_pane(), types tmux 3.7's new server, session, window, and pane options, exposes the new pane format variables on Pane, and wraps tmux 3.7's new command flags. Every 3.7-only surface is version-gated, so tmux 3.2a–3.6 keep working unchanged.
What's new
Floating panes (new-pane) (#694)
tmux 3.7's flagship feature is now reachable through Window.new_pane() and Pane.new_pane(). A floating pane sits above the tiled layout like a popup but behaves like a real pane. Pass width/height to size it and x/y to position it, alongside the usual shell, start_directory, environment, zoom, and empty, plus style, active_border_style, inactive_border_style, message, and keep (remain-on-exit). The returned Pane reports pane_floating_flag == "1". Requires tmux 3.7+; see the floating panes guide.
Typed tmux 3.7 options (#694)
The option dataclasses now type tmux 3.7's new options. On the server, get-clipboard; on the session, focus-follows-mouse, message-format, and prompt-command-cursor-style; and on the window, the copy-mode line-number options, the tree-mode preview options, and the window-pane status formats (of these, tree-mode-preview-format is also pane-scoped). The pane remain-on-exit option gained tmux 3.7's key value, and pane-active-border-style / pane-border-style (widened to pane scope in 3.7) now type on the pane too.
tmux 3.7 pane format variables (#694)
Pane now exposes tmux 3.7's new pane format variables: the floating-pane geometry and flags (pane_floating_flag, pane_x, pane_y, pane_z, pane_flags, pane_zoomed_flag), the OSC 9;4 progress report (pane_pb_progress, pane_pb_state), pane_pipe_pid, and the bracket_paste_flag and synchronized_output_flag screen-mode flags. Each is version-gated, so the format template stays clean on tmux 3.2a–3.6.
tmux 3.7 command flags (#694)
New tmux 3.7 flags are exposed on existing wrappers: Pane.capture_pane() gains hyperlinks (-H), line_numbers (-L), and line_flags (-F); Window.split() / Pane.split() gain empty (-E) plus style/active_border_style/inactive_border_style (-s/-S/-R), message (-m), and keep (-k); Session.kill() gains group (-g); and Pane.paste_buffer() gains no_vis (-S). On the server, Server.command_prompt() gains no_freeze (-C), Server.list_keys() gains format_ (-F), Server.run_shell() accepts trailing args expanded as #{1}/#{2}, and Server.refresh_client() gains request_clipboard (-l). Each warns and is ignored on tmux < 3.7.
Compatibility
Purely additive — no breaking changes. New 3.7 parameters are gated on tmux's reported version; unsupported flags warn and are ignored on older tmux, and floating-pane creation raises there because new-pane does not exist before 3.7. tmux 3.2a–3.6 remain supported and tested.
Links: Changelog · API docs · Floating panes guide · PyPI
What's Changed
Full Changelog: v0.59.0...v0.60.0
v0.59.0 - tmux 3.7 support
What's Changed
Full Changelog: v0.58.1...v0.59.0
v0.58.1 - pytest 9.1 compatibility
What's Changed
libtmux 0.58.1 restores compatibility with pytest 9.1. libtmux's bundled pytest plugin no longer aborts at import time, so projects that rely on libtmux's fixtures can move to the latest pytest without their test suite failing before collection.
pytest 9.1 rejects marks applied to fixture functions during plugin import — before collection begins. Because the plugin ships as an installed entry point, this aborted the entire test session for any downstream project that has libtmux installed (for example, tmuxp). The fix removes a no-op skipif mark from the zshrc fixture; the real zsh handling lives in conftest.py and is unchanged, so behavior is identical for zsh and non-zsh users.
This is a bug-fix-only patch release, per libtmux's pre-1.0 version policy (patch = bug fixes; minor = features/breaking changes).
See the CHANGES entry for full details.
Full Changelog: v0.58.0...v0.58.1
v0.58.0
libtmux 0.58.0 fixes subprocess output decoding on non-UTF-8 locales. Both tmux_cmd and ControlMode now enforce UTF-8 when reading tmux output, matching tmux's own encoding contract.
Fixes
Subprocess encoding on non-UTF-8 locales (#679)
tmux_cmd and ControlMode now pass encoding="utf-8" to subprocess.Popen, ensuring tmux output is decoded correctly regardless of the system locale. Previously, on non-UTF-8 locales, the FORMAT_SEPARATOR character (U+241E) was corrupted during decoding, causing list accessors (Server.sessions, etc.) to return empty results.
Links: Changelog · API docs · PyPI
Full Changelog: v0.57.1...v0.58.0
v0.57.1
What's Changed
libtmux 0.57.1 restores the lenient-by-default behavior for Server.sessions and Server.clients. Both accessors return an empty QueryList on tmux failure (socket permission errors, subprocess crashes, missing daemon) again,
matching the contract Server.sessions has carried since 0.17.0.
Code written for 0.57.0 with try/except LibTmuxException around these accessors keeps working — the except clause just becomes unreachable. For an explicit connectivity signal, use Server.is_alive() or Server.raise_if_dead().
Server.search_sessions, Server.search_windows, and Server.search_panes continue to raise — they take a caller-supplied filter where a tmux error carries meaningful signal.
See the CHANGES entry for full details.
Full Changelog: v0.57.0...v0.57.1
v0.57.0
libtmux 0.57.0 broadens tmux support around attached clients, tmux-native filtering, and format-token fields. Client gives callers a typed object for attached terminals, search_*() methods let tmux return only matching sessions, windows, and panes, and more tmux format tokens are exposed as typed attributes. LibTmuxException now records which tmux subcommand failed, making command errors easier to handle downstream.
Full release notes: https://libtmux.git-pull.com/history.html#libtmux-0-57-0-2026-05-17
Breaking changes
LibTmuxException string form gains a subcommand prefix
When LibTmuxException is raised from a libtmux method, str(exc) now begins with the originating tmux subcommand name followed by ": ". An error from Session.last_window() used to render as "can't find window" and now renders as "last-window: can't find window".
exc.args[0] still carries the original tmux error text, and the new LibTmuxException.subcommand attribute exposes the tmux subcommand name as a separate field.
The error-payload type also changed: exc.args[0] is now str, joined with "\n" when tmux emitted multiple stderr lines. Previously it was list[str]. Code that pattern-matches on str(exc) exactly, anchors a regex with ^ against the old shape, or indexes exc.args[0] element-by-element will no longer match. Substring matches ("can't find" in str(exc)) and unanchored re.search patterns continue to work unchanged.
See the migration guide for the upgrade pattern.
Server.sessions, Server.clients, Server.search_sessions raise on tmux errors
Previously, a tmux command failure under these accessors could return an empty QueryList indistinguishable from "no sessions / no clients attached" or "filter matched nothing". They now let LibTmuxException propagate for real tmux failures.
Genuine empty results — a server with no attached clients, or a filter that matched zero sessions — still return an empty QueryList. A missing or not-yet-started tmux server is also still treated as an empty result, preserving the historic contract that a fresh Server can be safely introspected before its daemon is up. Other tmux errors, such as socket permission failures or unsupported flags, now surface.
What's new
Clientobject andServer.clientsaccessor. New typed dataclass for tmux's attached-client model.client_nameis the stable identifier;attached_session/attached_window/attached_panere-readlist-clientsbefore resolving.attached_panefollows the attached session's current window — that can differ from the per-client active pane set byselect-pane -P.Server.display_messageandWindow.display_message. Server reads like#{version}and#{socket_path}work without a pane handle; window reads (#{window_zoomed_flag},#{window_active_clients_list}) auto-bind to the window's id. All threedisplay_messagewrappers (including the existingPane.display_message) surface tmux stderr viawarnings.warnrather than dropping it silently.- tmux-native filtering with
search_*().Server.search_panes/search_windows/search_sessionsplus the Session and Window analogues take afilter=kwarg routed to tmux's-fflag. tmux evaluates the expression and returns only matching objects. Caveat: tmux silently expands a malformed expression to empty, which it treats as false — a typo looks identical to "no matches". Pane.send_keys(cmd=None, …)flag-only invocation. Passcmd=Nonetogether withreset=Trueorrepeat=Nto invoke tmux's flag-onlysend-keys -R/send-keys -N <n>form without any trailing key argument.Server.list_buffers(format_string=, filter=). Project a chosen-Ftemplate or push a buffer-name match expression through tmux's format engine — same bad-filter caveat as thesearch_*methods.Server.run_shell(cwd=, show_stderr=). New kwargs map to tmux's-c(3.4+) and-E(3.6+) flags. Older tmux warns and ignores the kwarg instead of erroring.Pane.capture_pane(pending=True). Return bytes tmux has read from the pane but not yet committed to the terminal — useful for diagnosing programs whose output stalls mid-sequence.- More format-token fields on tmux objects.
Pane/Window/Session/Clientexpose more typed attributes for pane state (pane_dead,pane_in_mode,pane_marked,pane_synchronized,pane_path,pane_pipe), window state (window_zoomed_flag,window_silence_flag,window_flags), session state (session_marked), and client state (client_session,client_readonly,client_termtype). Tokens the running tmux does not support stayNoneinstead of making the listing fail.
Fixes
Pane.reset()now clears pane scrollback. In 0.56.0 the history clear silently no-op'd, leaving the scrollback intact (#650).
Documentation
- New API page:
libtmux.client. - New Format-Token Fields topic explains why some fields describe an active child object — for example,
session.pane_idreflects the active pane in the session's current window.
What's Changed
- CHANGES(docs): rewrite release history for May 2026 by @tony in #669
- Increase tmux coverage: Client, typed fields, Native filtering by @tony in #672
Full Changelog: v0.56.0...v0.57.0
libtmux on master [?] ❯ cat release.md
libtmux 0.57.0 broadens tmux support around attached clients, tmux-native filtering, and format-token fields. Client gives callers a typed object for attached terminals, search_*() methods let tmux return only matching sessions, windows, and panes, and more tmux format tokens are exposed as typed attributes. LibTmuxException now records which tmux subcommand failed, making command errors easier to handle downstream.
Full release notes: https://libtmux.git-pull.com/history/#libtmux-0-57-0-2026-05-17
Breaking changes
LibTmuxException string form gains a subcommand prefix
When LibTmuxException is raised from a libtmux method, str(exc) now begins with the originating tmux subcommand name followed by ": ". An error from Session.last_window() used to render as "can't find window" and now renders as "last-window: can't find window".
exc.args[0] still carries the original tmux error text, and the new LibTmuxException.subcommand attribute exposes the tmux subcommand name as a separate field. raise_if_stderr() is the shared helper that populates both.
The error-payload type also changed: exc.args[0] is now str, joined with "\n" when tmux emitted multiple stderr lines. Previously it was list[str]. Code that pattern-matches on str(exc) exactly, anchors a regex with ^ against the old shape, or indexes exc.args[0] element-by-element will no longer match. Substring matches ("can't find" in str(exc)) and unanchored re.search patterns continue to work unchanged.
See the migration guide for the upgrade pattern.
Server.sessions, Server.clients, Server.search_sessions raise on tmux errors
Previously, a tmux command failure under Server.sessions, Server.clients, or Server.search_sessions() could return an empty QueryList indistinguishable from "no sessions / no clients attached" or "filter matched nothing". They now let LibTmuxException propagate for real tmux failures.
Genuine empty results — a server with no attached clients, or a filter that matched zero sessions — still return an empty QueryList. A missing or not-yet-started tmux server is also still treated as an empty result, preserving the historic contract that a fresh Server can be safely introspected before its daemon is up. Other tmux errors, such as socket permission failures or unsupported flags, now surface.
What's new
Clientobject andServer.clientsaccessor. New typed dataclass for tmux's attached-client model.client_nameis the stable identifier;attached_session/attached_window/attached_panere-readlist-clientsbefore resolving.attached_panefollows the attached session's current window — that can differ from the per-client active pane set byselect-pane -P.- **
Server.display_messageand [Window.display_message...
v0.56.0
The tmux command parity release. ~50 new public methods land across Server, Pane, Window, and Session, plus expanded flag coverage on existing wrappers, plus a control_mode test fixture that lets commands requiring an attached client run under CI without a TTY. Net result: callers no longer need to drop down to Server.cmd(...) for the commands tmux ships out of the box.
No breaking changes — public API is purely additive.
Highlights
Interactive tmux commands now scriptable
New wrappers for commands that require an attached client:
Pane.display_popup,Pane.display_panesPane.choose_buffer,Pane.choose_client,Pane.choose_treeServer.display_menu,Server.command_prompt,Server.confirm_beforeSession.detach_client,Server.detach_client,Server.detach_all_clients
The three detach-client wrappers each map to one tmux flag group exactly, with a single subprocess call:
| Wrapper | tmux invocation | Scope |
|---|---|---|
Session.detach_client() |
tmux detach-client -s <session_id> |
every client in this session |
Server.detach_client(target_client=...) |
tmux detach-client [-t <client>] |
server-wide single-client lookup |
Server.detach_all_clients(target_client=...) |
tmux detach-client -a [-t <keep>] |
server-wide, optionally preserving one client |
tmux buffer I/O suite
Round-trip pane content through named tmux buffers — useful for clipboard interop and inter-process data hand-off:
Server.set_buffer,Server.show_buffer,Server.delete_bufferServer.save_buffer,Server.load_buffer,Server.list_buffersPane.paste_buffer
Key bindings, shell execution, and client management
- Server:
bind_key,unbind_key,list_keys,list_commands,run_shell,if_shell,source_file,list_clients,start_server,lock_server,lock_client,refresh_client,suspend_client,server_access,show_messages,show_prompt_history,clear_prompt_history - Session:
lock_session - Pane:
send_prefix
Window and pane manipulation parity
- Window:
swap,link,unlink,respawn,last_pane,next_layout,previous_layout,rotate - Pane:
swap,join,break_pane,move,pipe,clear_history,respawn,copy_mode,clock_mode,customize_mode,find_window - Server:
wait_for - Session:
last_window,next_window,previous_window
Filled-in flag coverage on existing methods
Most pre-existing wrappers now expose the remaining tmux flags:
Pane.send_keys—literal,hex_keys,key_name,expand_formats,target_client,repeat,reset,copy_mode_cmdPane.split—percentage=Pane.capture_pane—alternate_screen,quiet,mode_screen,to_buffer(writes capture into a tmux buffer instead of returning it)- <a href="https://libt...
v0.55.1 - improved docs, pytest teardown
A point release focused on a pytest-plugin cleanup fix, a new Sphinx extension for documenting pytest fixtures, and a docs-stack migration to gp-sphinx.
Highlights
Fix: pytest_plugin leaks tmux socket files on teardown (#661, fixes #660)
The server and TestServer fixtures now unlink(2) the tmux socket from /tmp/tmux-<uid>/ during teardown, in addition to calling server.kill(). tmux does not reliably remove its own socket on non-graceful exit, so /tmp/tmux-<uid>/ would accumulate stale libtmux_test* entries across runs — 10k+ observed on long-lived dev machines.
A new internal _reap_test_server helper centralizes the kill + unlink flow and suppresses cleanup-time errors, so a finalizer failure can no longer mask the real test failure.
If you use libtmux's pytest plugin in CI or locally, upgrade to stop the leak.
New: Sphinx extension for pytest fixture documentation (#656)
A new Sphinx extension (docs/_ext/sphinx_pytest_fixtures.py) renders pytest fixtures as first-class API documentation with scope/kind/factory badges, cross-referenced dependencies, and auto-generated usage snippets.
.. autofixture::— autodoc-style documenter for individual fixtures.. autofixtures::— bulk discovery and rendering from a module.. autofixture-index::— auto-generated index table with linked return types (via intersphinx) and parsed RST descriptions:fixture:cross-reference role with short-name resolution- Scope, kind, and autouse badges with touch-accessible tooltips
- Responsive layout (mobile metadata stacking, badge font scaling, scroll wrappers)
- WCAG AA contrast compliance in both light and dark mode
- 109 tests (unit + integration)
Also fixes an inaccurate session_params fixture docstring surfaced by the new extension.
Documentation
- Docs restructured to the Library Skeleton pattern (#652)
- Self-hosted fonts, eliminated layout shift, added SPA-style navigation (#643)
- Standardized shell code-block formatting across all docs (#644)
- Migrated the docs stack to gp-sphinx workspace packages (#657)
- Visual improvements to API docs via gp-sphinx (#658)
- Bumped gp-sphinx to
v0.0.1a8(#659); subsequent bump tov0.0.1a9on master
Development
- Added
types-docutilsto dev dependencies for mypy type checking (#656)
What's Changed
- docs: self-host fonts, eliminate layout shift, add SPA navigation by @tony in #643
- docs(style[shell]): Standardize shell code blocks by @tony in #644
- docs(redesign): restructure documentation to Library Skeleton pattern by @tony in #652
- feat(_ext[sphinx_pytest_fixtures]): Sphinx extension for pytest fixture documentation by @tony in #656
- docs: Migrate to gp-sphinx workspace packages by @tony in #657
- docs(feat[api-style]): Visual improvements to API docs via gp-sphinx by @tony in #658
- chore(docs): adopt gp-sphinx v0.0.1a8 by @tony in #659
- fix(pytest_plugin): unlink socket file on fixture teardown by @tony in #661
Full Changelog: v0.55.0...v0.55.1