Releases: tmux-python/libtmux
v0.53.0 - Bug fixes
A focused maintenance release that fixes a critical bug in Session.attach() that caused tracebacks when users killed sessions while attached via tmuxp load.
Highlights
- Fixed:
Session.attach()no longer raisesTmuxObjectDoesNotExistwhen a user kills the session during attachment - Breaking:
Session.attach()no longer callsrefresh()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-sessionblocks 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 itTimeline
| 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 |
Installation
pip:
pip install libtmux==0.53.0uv:
uv add libtmux==0.53.0pipx (for tmuxp users):
pipx upgrade tmuxpWhat's Changed
Full Changelog: v0.52.1...v0.53.0
v0.52.1 - Trusted Publisher for PyPI builds
Development
Full Changelog: v0.52.0...v0.52.1
v0.52.0 - `send_keys()` updates
libtmux 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
Full Changelog: v0.51.0...v0.52.0
v0.51.0 (Breaking API deprecations)
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.
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_nameLinks
Full Changelog: v0.50.1...v0.51.0
v0.50.1 - Maintenance release
v0.50.0 - options and hook management
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(), andunset_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
OptionScopeenum:Server,Session,Window,PaneOPTION_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
Full Changelog: v0.49.0...v0.50.0
v0.49.0 (drop tmux < 3.2)
v0.48.0 (deprecating tmux <3.2)
What's Changed
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
Full Changelog: v0.47.0...v0.48.0
v0.47.0 - Drop Python 3.9
Breaking changes
Full Changelog: v0.46.2...v0.47.0
v0.46.2 - `start_directory` typing fix
What's Changed
- Fix
new_windowargument typing inSessionby @Data5tream in #596
New Contributors
- @Data5tream made their first contribution in #596
Full Changelog: v0.46.1...v0.46.2