* Mon Mar 09 2026 opensuse_buildservice@ojkastl.de
- update to 0.55.0:
* Pane.set_title()
- New Pane.set_title() method wraps select-pane -T and returns
the pane for method chaining. A Pane.title property aliases
pane_title for convenience:
pane.set_title("my-worker")
pane.pane_title # 'my-worker'
pane.title # 'my-worker'
The pane_title format variable is now included in libtmux's
pane format queries (it was previously excluded via an
incorrect "removed in 3.1+" comment).
* Configurable tmux binary path
- Server now accepts a tmux_bin parameter to use an alternative
binary (e.g. wemux, byobu, or a custom build):
server = Server(socket_name="myserver", tmux_bin="/usr/local/bin/tmux-next")
The path is threaded through Server.cmd(),
Server.raise_if_dead(), fetch_objs(), all version-check
functions (has_version, has_gte_version, etc.), and hook
scope guards in HooksMixin. Child objects (Session, Window,
Pane) inherit it automatically. Falls back to
shutil.which("tmux") when not set.
* Pre-execution command logging
- tmux_cmd now emits a structured DEBUG log record with
extra={"tmux_cmd": ...} before invoking the subprocess, using
shlex.join for POSIX-correct quoting. This complements the
existing post-execution stdout log and is a prerequisite for
a future dry-run mode.
* Bug fix: TmuxCommandNotFound raised for invalid tmux_bin path
- Passing a non-existent binary path previously surfaced as a
raw FileNotFoundError from subprocess. Both tmux_cmd and
raise_if_dead now catch FileNotFoundError and raise
TmuxCommandNotFound consistently.
- update to 0.54.0:
* Highlights
- Structured lifecycle logging across Server, Session, Window,
and Pane with filterable extra context
- Bug fixes for rename_window(), Server.kill(), new_session(),
and kill_window() error handling
* Structured lifecycle logging (#637)
- All lifecycle operations (create, kill, rename, split) now
emit INFO-level log records with structured extra context.
Every log call includes scalar keys for filtering in log
aggregators and test assertions via caplog.records:
Key Type Context
tmux_subcommand str tmux subcommand (e.g. new-session)
tmux_target str tmux target specifier
tmux_session str session name
tmux_window str window name or index
tmux_pane str pane identifier
* Logging hygiene improvements:
- NullHandler added to library __init__.py per Python logging
best practices
- DEBUG-level structured logs for tmux_cmd execution with
isEnabledFor guards and heavy keys (tmux_stdout, tmux_stderr,
tmux_stdout_len, tmux_stderr_len)
- Lazy formatting: replaced f-string log formatting with %s
throughout
- Diagnostics: replaced traceback.print_stack() with
logger.debug(exc_info=True)
- Options warnings: replaced logger.exception() with
logger.warning() and tmux_option_key structured context for
recoverable parse failures
- Cleanup: removed unused logger definitions from modules that
don't log
* Bug fixes
- Window.rename_window() now raises on failure (#637)
Previously rename_window() caught all exceptions and logged
them, masking tmux errors. It now propagates the error,
consistent with all other command methods.
- Server.kill() captures stderr (#637)
Server.kill() previously discarded the tmux return value. It
now checks stderr, raises on unexpected errors, and silently
returns for expected conditions ("no server running", "error
connecting to").
- Server.new_session() checks kill-session stderr (#637)
When kill_session=True and the existing session kill fails,
new_session() now raises LibTmuxException with the stderr
instead of proceeding silently.
- Session.kill_window() target formatting fix (#637)
Fixed self.window_name (wrong attribute) to self.session_name
when formatting tmux targets for integer target_window
values. Also widened the type signature from str | None to
str | int | None to match the existing
isinstance(target_window, int) branch.
* Thu Feb 19 2026 opensuse_buildservice@ojkastl.de
- update to 0.53.1:
* Bug fixes
- Fix race condition in new_session() by avoiding list-sessions
query by @neubig in #625
* Development
- build: Migrate from Makefile to justfile by @tony in #617
* Mon Dec 15 2025 opensuse_buildservice@ojkastl.de
- update to 0.53.0:
* Highlights
- Fixed: Session.attach() no longer raises
TmuxObjectDoesNotExist when a user kills the session during
attachment
- Breaking: Session.attach() no longer calls refresh() after
returning (semantically incorrect for interactive commands)
* Bug Fixes
- Session.attach() no longer fails if session killed during
attachment (#616)
Fixed an issue where Session.attach() would raise
TmuxObjectDoesNotExist when a user:
- Attaches to a tmux session via tmuxp load
- Works in the session
- Kills the session (e.g., closes all windows) before
detaching
- Detaches from tmux
User Experience (Before Fix)
After running tmuxp load, users would see this traceback
printed to their terminal after detaching:
Traceback (most recent call last):
File "~/.local/bin/tmuxp", line 7, in <module>
sys.exit(cli.cli())
...
File ".../libtmux/session.py", line 332, in attach
self.refresh()
File ".../libtmux/neo.py", line 167, in _refresh
obj = fetch_obj(...)
File ".../libtmux/neo.py", line 242, in fetch_obj
raise exc.TmuxObjectDoesNotExist(...)
libtmux.exc.TmuxObjectDoesNotExist: Could not find object
Root Cause
Session.attach() called self.refresh() after the
attach-session command returned. Since attach-session is a
blocking interactive command, the session state can change
arbitrarily during attachment—including being killed
entirely.
Technical Details
The refresh() call was semantically incorrect for interactive
commands:
- attach-session blocks until user detaches
- Session state can change during attachment (user may kill
session, rename it, etc.)
- Refreshing the object after such a command makes no
sense—the state could be anything
The fix: Remove the self.refresh() call from Session.attach()
(2 lines removed).
* Breaking Changes
- Session.attach() no longer calls refresh() (#616)
Session.attach() previously called Session.refresh() after
the attach-session command returned. This was semantically
incorrect since attach-session is a blocking interactive
command where session state can change arbitrarily during
attachment.
This was never strictly defined behavior as libtmux abstracts
tmux internals away. Code that relied on the session object
being refreshed after attach() should explicitly call
session.refresh() if needed.
Migration
[#] If you relied on the implicit refresh (unlikely):
session.attach()
session.refresh() # Now explicit if you need it
Timeline
Date Event
Feb 2024 Session.attach() added with refresh() call (9a5147a)
Nov 2025 tmuxp switched from attach_session() to attach() (fdafdd2b)
Dec 2025 Users started experiencing the bug
Dec 2025 v0.53.0 released with fix
* What's Changed
- fix(Session.attach()): Remove refresh() call that fails after
session killed by @tony in #616
* Tue Dec 09 2025 opensuse_buildservice@ojkastl.de
- update to 0.52.1:
* Development
- ci(release): Migrate to PyPI Trusted Publisher by @tony in #615
- update to 0.52.0:
* capture_pane() enhancements
- The Pane.capture_pane() method now supports 5 new parameters
exposing additional tmux capture-pane flags:
Parameter tmux Flag Description
escape_sequences -e Include ANSI escape sequences (colors, attributes)
escape_non_printable -C Escape non-printable chars as octal \xxx
join_wrapped -J Join wrapped lines back together
preserve_trailing -N Preserve trailing spaces at line ends
trim_trailing -T Trim trailing empty positions (tmux 3.4+)
Examples
Capturing colored output:
[#] Capture with ANSI escape sequences preserved
pane.send_keys('printf "\\033[31mRED\\033[0m"', enter=True)
output = pane.capture_pane(escape_sequences=True)
[#] Output contains: '\x1b[31mRED\x1b[0m'
Joining wrapped lines:
[#] Long lines that wrap are joined back together
output = pane.capture_pane(join_wrapped=True)
- Version compatibility
The trim_trailing parameter requires tmux 3.4+. If used
with an older version, a warning is issued and the flag is
ignored. All other parameters work with libtmux's minimum
supported version (tmux 3.2a).
* What's Changed
- Capture pane updates by @tony in #614
- update to 0.51.0:
* Breaking Changes
- Deprecate legacy APIs
Legacy API methods (deprecated in v0.16–v0.33) now raise
DeprecatedError (hard error) instead of emitting
DeprecationWarning.
See the migration guide for full context and examples.
- Deprecate legacy APIs (raise DeprecatedError) by @tony in
[#611]
Method Renamings
Deprecated Replacement Class Deprecated Since
kill_server() kill() Server 0.30.0
attach_session() attach() Session 0.30.0
kill_session() kill() Session 0.30.0
select_window() select() Window 0.30.0
kill_window() kill() Window 0.30.0
split_window() split() Window 0.33.0
select_pane() select() Pane 0.30.0
resize_pane() resize() Pane 0.28.0
split_window() split() Pane 0.33.0
Property Renamings
Deprecated Replacement Class Deprecated Since
attached_window active_window Session 0.31.0
attached_pane active_pane Session 0.31.0
attached_pane active_pane Window 0.31.0
Query/Filter API Changes
Deprecated Replacement Class Deprecated Since
list_sessions() / '_list_sessions(') sessions property Server 0.17.0
list_windows() / '_list_windows()' windows property Session 0.17.0
list_panes() / '_list_panes()' panes property Window 0.17.0
where({...}) .filter(**kwargs) on sessions/windows/panes All 0.17.0
find_where({...}) .get(default=None, **kwargs) on sessions/windows/panes All 0.17.0
get_by_id(id) .get(session_id/window_id/pane_id=..., default=None) All 0.16.0
children property sessions/windows/panes All 0.17.0
Attribute Access Changes
Deprecated Replacement Deprecated Since
obj['key'] obj.key 0.17.0
obj.get('key') obj.key 0.17.0
obj.get('key', None) getattr(obj, 'key', None) 0.17.0
Still Soft Deprecations (DeprecationWarning)
The following deprecations from v0.50.0 continue to emit DeprecationWarning only:
Deprecated Replacement Class
set_window_option() set_option() Window
show_window_option() show_option() Window
show_window_options() show_options() Window
g parameter global_ parameter Options & hooks methods
Migration Example
Before (deprecated, now raises DeprecatedError):
[#] Old method names
server.kill_server()
session.attach_session()
window.split_window()
pane.resize_pane()
[#] Old query API
server.list_sessions()
session.find_where({'window_name': 'main'})
[#] Old dict-style access
window['window_name']
After:
[#] New method names
server.kill()
session.attach()
window.split()
pane.resize()
[#] New query API
server.sessions
session.windows.get(window_name='main', default=None)
[#] New attribute access
window.window_name
- update to 0.50.1:
* Documentation
- Doc fixes by @tony in #612
* Fri Dec 05 2025 opensuse_buildservice@ojkastl.de
- update to 0.50.0:
libtmux 0.50 brings a major enhancement to option and hook management with a unified, typed API for managing tmux options and hooks across all object types.
* Highlights
- Unified Options API: New show_option(), show_options(), set_option(), and unset_option() methods available on Server, Session, Window, and Pane
- Hook Management: Full programmatic control over tmux hooks with support for indexed hook arrays and bulk operations
- SparseArray: New internal data structure for handling tmux's sparse indexed arrays (e.g., command-alias[0], command-alias[99])
- tmux 3.2+ baseline: Removed support for tmux versions below 3.2a, enabling cleaner code and full hook/option feature support
* Unified Options API
All tmux objects now share a consistent options interface:
import libtmux
server = libtmux.Server()
session = server.sessions[0]
window = session.windows[0]
[#] Get all options as a structured dict
session.show_options()
[#] {'activity-action': 'other', 'base-index': 0, ...}
[#] Get a single option value
session.show_option('base-index')
[#] 0
[#] Set an option
window.set_option('automatic-rename', True)
[#] Unset an option (revert to default)
window.unset_option('automatic-rename')
* Hook Management
Programmatic control over tmux hooks:
session = server.sessions[0]
[#] Set a hook
session.set_hook('session-renamed', 'display-message "Renamed!"')
[#] Get hook value (returns SparseArray for indexed hooks)
session.show_hook('session-renamed')
[#] {0: 'display-message "Renamed!"'}
[#] Get all hooks
session.show_hooks()
[#] Remove a hook
session.unset_hook('session-renamed')
[#] Bulk operations for indexed hooks
session.set_hooks('session-renamed', {
0: 'display-message "Hook 0"',
1: 'display-message "Hook 1"',
5: 'run-shell "echo hook 5"',
})
* Breaking Changes
- Deprecated Window methods
The following methods are deprecated and will be removed in a
future release:
Deprecated Replacement
Window.set_window_option() Window.set_option()
Window.show_window_option() Window.show_option()
Window.show_window_options() Window.show_options()
* Deprecated g parameter
The g parameter for global options is deprecated in favor of
global_:
[#] Before (deprecated)
session.show_option('status', g=True)
[#] After (0.50.0+)
session.show_option('status', global_=True)
* New Constants
- OptionScope enum: Server, Session, Window, Pane
- OPTION_SCOPE_FLAG_MAP: Maps scope to tmux flags (-s, -w, -p)
- HOOK_SCOPE_FLAG_MAP: Maps scope to hook flags
* Documentation
- New topic guide: Options and Hooks
- New topic guides: Automation patterns, Workspace setup, Pane
interaction, QueryList filtering
- Refreshed README with hero section, quickstart, and more
examples
* tmux Version Compatibility
Feature Minimum tmux
All options/hooks features 3.2+
Window/Pane hook scopes (-w, -p) 3.2+
client-active, window-resized hooks 3.3+
pane-title-changed hook 3.5+
* What's Changed
- Improved option management, add hook management by @tony in
[#516]
- Refresh README by @tony in #609
- update to 0.49.0:
* Breaking: tmux <3.2 fully dropped
- Drop support for tmux versions < 3.2 by @tony in #608
- update to 0.48.0:
* Breaking: tmux <3.2 deprecated
- Deprecate old tmux versions by @tony in #606
* Development
- tmux: Add tmux 3.6 to testgrid by @tony in #607
* Sun Nov 02 2025 opensuse_buildservice@ojkastl.de
- update to 0.47.0:
* Breaking changes
- Support Python 3.14 by @tony in #601
- Drop Python 3.9 by @tony in #602
* Thu May 29 2025 opensuse_buildservice@ojkastl.de
- update to 0.46.2:
* Fix new_window argument typing in Session by @Data5tream in #596
- types: Add StrPath typing, fix new_session by @tony in #597
- types: Add StrPath typing, fix new_session, part 2 by @tony
in #598
* Sun Mar 16 2025 opensuse_buildservice@ojkastl.de
- update to 0.46.1:
* Documentation
- Fix typo in Pane.send_keys (#593)
* Wed Feb 26 2025 opensuse_buildservice@ojkastl.de
- update to 0.46.0:
* Breaking Changes
- Test Helper Imports Refactored: Direct imports from
libtmux.test are no longer possible. You must now import from
specific submodules (#580)
[#] Before:
from libtmux.test import namer
[#] After:
from libtmux.test.named import namer
[#] Before:
from libtmux.test import RETRY_INTERVAL_SECONDS
[#] After:
from libtmux.test.constants import RETRY_INTERVAL_SECONDS
* Internal Improvements
- Enhanced Test Utilities: The EnvironmentVarGuard now handles
variable cleanup more reliably
- Comprehensive Test Coverage: Added test suites for constants
and environment utilities
- Code Quality: Added proper coverage markers to exclude type
checking blocks from coverage reports
- Documentation: Improved docstrings and examples in the random
module
These changes improve maintainability of test helpers both
internally and for downstream packages that depend on libtmux.
* What's Changed
Coverage: Test helpers by @tony in #580
* Mon Feb 24 2025 opensuse_buildservice@ojkastl.de
- update to 0.45.0:
* Breaking Changes
- Test helpers: Refactor by @tony in #578
Test helper functionality has been split into focused modules
(#578):
libtmux.test module split into:
- libtmux.test.constants: Test-related constants
(TEST_SESSION_PREFIX, etc.)
- libtmux.test.environment: Environment variable mocking
- libtmux.test.random: Random string generation utilities
- libtmux.test.temporary: Temporary session/window management
- Breaking: Import paths have changed. Update imports:
[#] Old (0.44.x and earlier)
from libtmux.test import (
TEST_SESSION_PREFIX,
get_test_session_name,
get_test_window_name,
namer,
temp_session,
temp_window,
EnvironmentVarGuard,
)
[#] New (0.45.0+)
from libtmux.test.constants import TEST_SESSION_PREFIX
from libtmux.test.environment import EnvironmentVarGuard
from libtmux.test.random import get_test_session_name, get_test_window_name, namer
from libtmux.test.temporary import temp_session, temp_window
* Misc
- Cursor rules: Add Cursor Rules for Development and Git Commit
Standards by @tony in #575
* CI
- tests(ci) Check runtime deps import correctly by @tony in
[#574]