| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
A pane whose `pane_current_path` contained a newline made `Server.panes` and `Server.windows` raise `ValueError: zip() argument 2 is shorter than argument 1` for the entire server, healthy panes included. `fetch_objs` iterated stdout one line per object, so a value containing a newline split its record across two lines and each fragment reached `parse_output` with too few values. Every pane row carries `pane_current_path` and every pane-targeting lookup enumerates panes, so one directory took out resolution for all of them. The blast radius also moved with the active pane, because session and window rows resolve `pane_*` against it — the same server appeared to work or fail as the user switched panes. Regrouping on the field separator is exact rather than merely better: the `-F` template terminates every field with one, so a record holds exactly `len(fields)` separators and a newline is never among them. Nothing is split on newlines any more, so a value may contain any number of them, in any position. The newline that terminated the previous record survives the rejoin glued to the next record's first value and is stripped as the delimiter it is. Regrouping also makes a forged separator detectable: a value count that is not a whole number of records now raises a `LibTmuxException` naming the cause instead of surfacing a `zip()` message. Reported against libtmux-mcp, where an agent hit it by cd-ing a pane into such a directory and then could not repair it through the MCP, because every tool that could have moved the pane needed the same enumeration.
Codecov Report❌ Patch coverage is 88.88889% with 2 lines in your changes missing coverage. Please review.
@@ Coverage Diff @@
## master #752 +/- ##
==========================================
+ Coverage 52.37% 52.53% +0.16%
==========================================
Files 26 26
Lines 3729 3746 +17
Branches 747 752 +5
==========================================
+ Hits 1953 1968 +15
- Misses 1472 1473 +1
- Partials 304 305 +1 ☔ View full report in Codecov by Harness.
|
Sorry, something went wrong.
A pane whose current directory contains a newline makes libtmux fail to parse `-F` output, and because every pane lookup enumerates panes, the whole server stops resolving — healthy panes included. It reached the agent as `Unexpected error: ValueError: zip() argument 2 is shorter than argument 1`, logged at ERROR, naming nothing it could act on. The agent could not repair it through the MCP either: every tool that could have moved the pane out needed the same enumeration. It is now an expected failure that names the cause, says the blast radius is server-wide rather than one pane, and gives the command that locates the offender. Matched on the message because the raise site is a stdlib `zip` with no dedicated exception type. The parse itself is fixed upstream in tmux-python/libtmux#752, but this diagnosis is kept rather than deferred: the floor is `libtmux>=0.62.0` and the installed version is not this package's to choose. `exc.PaneNotFound` prefixes its own message and the mapper prefixed it again, so the most frequently hit error in the server read `Pane not found: Pane not found: %9999`.
| Back | FazBrowse Home | New Git URL |
A pane whose pane_current_path contains a newline — a directory whose name has one — makes Server.panes and Server.windows raise ValueError: zip() argument 2 is shorter than argument 1 for the entire server, healthy panes included.
Reproduce
Cause
fetch_objs iterated stdout one line per object:
tmux writes one record per line, but a value containing a newline splits that record across two output lines. Each fragment then reaches parse_output with fewer values than the template has fields, and its zip(..., strict=True) raises.
Two properties make this worse than a single bad pane:
Fix
Records are regrouped on the field separator instead of on newlines. This is exact rather than merely better: the -F template from get_output_format terminates every field with a separator, so one record holds exactly len(fields) separators and a newline is never one of them. Nothing is split on newlines any more, so a value may contain any number of them, in any position. The newline that terminated the previous record survives the rejoin glued to the next record's first value, and is stripped as the delimiter it is.
Regrouping also makes a forged separator detectable. A value that contains the separator itself previously corrupted the parse silently; a value count that is not a whole number of records now raises a LibTmuxException naming the cause.
Tests
_split_records is covered for a newline in the first, middle, and last field, consecutive newlines, a poisoned record between clean ones, empty values, zero records, and the forged-separator error.
Verified end to end against a real poisoned server: sessions, windows, and panes all enumerate, and the value round-trips exactly as '/tmp/evil\ndir'.
Provenance
Found while auditing libtmux-mcp, where an agent that cd-ed a pane into such a directory could not repair it through the MCP at all — every tool that could have moved the pane back needed the same enumeration.
tests/test_server.py::test_new_session_shell_env fails in my local environment before and after this change (new-session: command too long, caused by an unusually large environment being passed as -e flags); it is unrelated.