Skip to content

argv parser drops -e args after a value ending in backslash + spaces #265

@marcfargas

Description

@marcfargas

Summary

psmux's argv parser does not correctly handle the standard Windows CommandLineToArgvW encoding for an argument whose value ends in a backslash and contains spaces. When such a value appears in tmux new-session -e KEY=VAL, all subsequent -e flags are swallowed into the first value's content.

This is independent of #264 (which we just filed for paste-buffer). Both surface only on Windows, both block the same downstream consumer (CAO).

Environment

  • psmux: 3.3.4 (psmux -Vpsmux 3.3.4), installed via scoop
  • Host: Windows 11 Pro 26200, ARM
  • Caller: Python 3 subprocess.run(cmd_list, ...) — uses list2cmdline to encode argv per Microsoft's documented rules

Minimal repro

# Run from a non-psmux shell (e.g., Git Bash with PSMUX_SESSION unset)
python -c "
import subprocess
cmd = ['tmux', 'new-session', '-s', 'bsrepro', '-n', 'w', '-c', r'C:\Users\YOU', '-d',
       '-e', r'TRAILING_BS=C:\Program Files\Foo Bar\plugins' + chr(92),
       '-e', 'NEXT_VAR=should_survive',
       '-e', 'CAO_TERMINAL_ID=test-id-12345']
subprocess.run(cmd)
"

tmux show-environment -t bsrepro

Observed

TRAILING_BS=C:\Program Files\Foo Bar\plugins" -e NEXT_VAR=should_survive -e CAO_TERMINAL_ID=test-id-12345

TRAILING_BS is set to the entire concatenation. NEXT_VAR and CAO_TERMINAL_ID are completely missing from the session env.

Expected

TRAILING_BS=C:\Program Files\Foo Bar\plugins\
NEXT_VAR=should_survive
CAO_TERMINAL_ID=test-id-12345

Real tmux on POSIX, and Windows tooling that uses CommandLineToArgvW, parse this correctly.

Why it's mis-parsed

Python's subprocess.list2cmdline correctly encodes MOZ_PLUGIN_PATH=...\plugins\ (value ending in \, value containing spaces) as:

"MOZ_PLUGIN_PATH=...\plugins\\"

Per Microsoft's argv parsing rules:

  • \\" → 1 literal backslash + close-quote (the \ is N backslashes where N=2; 2N/2 = 1 literal backslash; the " closes the string).

psmux's parser appears to treat \\" as if it were \" (i.e., escaped-quote-keep-going), so the string never closes and consumes the following -e NEXT_VAR=... and -e CAO_TERMINAL_ID=....

Trigger conditions

The bug fires when an env value:

  1. Contains whitespace (forces Python to wrap in quotes), AND
  2. Ends with \.

On Windows this is common — MOZ_PLUGIN_PATH, some *_PATH vars, and various Program Files\…\ entries can end in trailing backslash.

Impact on CAO

CAO sets CAO_TERMINAL_ID last in the env dict. Any earlier env var matching the trigger silently swallows it, leaving the spawned agent without its terminal identifier — breaking assign/handoff MCP tools.

CAO-side workaround

We're stripping trailing backslashes from env values before passing as -e KEY=VAL (lossless for path-like vars: C:\Foo and C:\Foo\ are equivalent on Windows). Will remove once this is fixed in psmux.

Reference

Filed downstream of awslabs/cli-agent-orchestrator#207. See also #264 for paste-buffer (separate issue).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions