Skip to content

send-keys race condition: command sent before shell finishes initializing #84

Description

@andrew-t-james-wc

Bug Description

When dmux creates a new tmux session, it immediately sends the startup command via tmux send-keys without waiting for the shell in the pane to finish initializing. If the user has a heavy shell init (e.g., antigen, mise, starship, zoxide, atuin, direnv), the send-keys command fires before the shell is ready, causing the PATH='...' '/path/to/dmux' command to be echoed as raw text into the terminal instead of being executed.

The result is that dmux appears to silently fail — the user just sees the PATH string dumped to the terminal with no dmux TUI.

Steps to Reproduce

  1. Have a shell with heavy initialization (e.g., zsh with antigen, mise, starship, zoxide, atuin, direnv)
  2. Run dmux — it creates a new tmux session and immediately sends keys
  3. Observe that the PATH='...' command string is printed as raw text rather than executed

Relevant Code Path

In dist/index.js:

  • Line 232: execSync('tmux new-session -d -s ${sessionName}', { stdio: 'inherit' }) — creates the session
  • Line 248: sendTmuxShellCommand(this.sessionName, dmuxCommand, 'inherit') — fires immediately after with no wait for shell readiness

sendTmuxShellCommand simply calls spawnSync('tmux', ['send-keys', '-t', target, command, 'Enter'], { stdio }).

The command sent is built by buildDmuxCommand in dist/utils/dmuxCommand.js and looks like PATH='...' '/path/to/dmux'.

There is no mechanism between session creation and send-keys to verify the shell has finished initializing and is ready to accept input.

Suggested Fix

A few possible approaches:

  1. Poll for shell readiness — after tmux new-session, poll the pane (e.g., check for a prompt character via tmux capture-pane) before sending keys
  2. Use tmux wait-for — have the shell signal readiness via tmux wait-for -S at the end of init, and wait on it before sending keys
  3. Use tmux respawn-pane -k — instead of send-keys, use respawn-pane with the command directly, which replaces the shell process entirely and avoids the race
  4. Use tmux set-option remain-on-exit + respawn-pane — start the pane with the dmux command directly rather than starting a shell and then injecting a command into it

Option 3 (respawn-pane) is likely the simplest and most robust, as it sidesteps the race entirely by not relying on shell readiness at all.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions