Skip to content

bug: prevent tmux project listing from failing on empty roots#91

Open
ALX99 wants to merge 2 commits into
masterfrom
bug/tmux-go-session-empty-project-root
Open

bug: prevent tmux project listing from failing on empty roots#91
ALX99 wants to merge 2 commits into
masterfrom
bug/tmux-go-session-empty-project-root

Conversation

@ALX99

@ALX99 ALX99 commented Jul 10, 2026

Copy link
Copy Markdown
Owner

Bug

.local/bin/tmux-go-session --list fails when a configured project root exists but contains no child directories. Bash leaves the unmatched "$d"/*/ pattern literal, and the script later tries to derive a tmux session name from the literal * basename.

Severity and impact

This is a reliability bug in the tmux project picker. An empty project root is a normal state on a new machine, after moving projects, or when one of several configured roots is temporarily empty. Instead of continuing with existing tmux sessions and the built-in ~/dotfiles / ~/.claude entries, the picker exits non-zero and cannot open.

Evidence

Relevant code on master:

  • .local/bin/tmux-go-session, emit_picker_rows() loops over for sub in "$d"/*/ without enabling nullglob or checking that each expanded value is a directory.
  • When $d is empty, Bash supplies the literal value $d/*/ to the loop.
  • After removing the trailing slash, ${sub##*/} is *.
  • Session-name sanitization converts * to _, strips the leading/trailing underscore, and produces an empty name.
  • The existing guard then prints Cannot derive a tmux session name for project: <root>/* and returns 1.

The added regression test creates an existing but empty project root, stubs tmux list-sessions, runs tmux-go-session --list, and verifies that listing succeeds without leaking the unmatched glob while preserving the built-in picker entries.

This is a real defect rather than a hypothetical edge case because an existing empty directory deterministically triggers Bash's default unmatched-glob behavior.

Reproduce

Setup:

git checkout master
tmp=$(mktemp -d)
mkdir -p "$tmp/bin" "$tmp/home/dotfiles" "$tmp/home/.claude" "$tmp/projects"
printf '#!/usr/bin/env bash\nexit 1\n' > "$tmp/bin/tmux"
chmod +x "$tmp/bin/tmux"

Triggering condition: PROJ_DIR names an existing directory with no child project directories.

Exact command:

HOME="$tmp/home" PROJ_DIR="$tmp/projects" PATH="$tmp/bin:$PATH" \
  .local/bin/tmux-go-session --list

Actual result on master:

Cannot derive a tmux session name for project: <tmp>/projects/*

The command exits non-zero.

Expected result:

The command exits successfully, ignores the unmatched project glob, and continues listing valid session/project entries.

Reviewer regression command:

bash tests/tmux-go-session-empty-project-root.test.sh

Root cause

The project enumeration loop assumes "$d"/*/ always expands to at least one real directory. Bash does not remove unmatched globs by default, so an empty root produces a literal glob string that is treated as a project path.

Fix

Add a directory guard at the start of the inner loop:

[[ -d $sub ]] || continue

This skips the unmatched literal and also defensively ignores any non-directory value without changing enumeration of real project directories.

Validation

Validation performed:

  • Checked existing open and closed pull requests for the same tmux-go-session empty-root/glob defect; no duplicate was found.
  • Checked open issues for the same defect; no duplicate was found.
  • Traced the baseline Bash expansion and sanitization path deterministically: empty root -> literal <root>/*/ -> basename * -> sanitized empty name -> existing error return.
  • Added tests/tmux-go-session-empty-project-root.test.sh, which stubs tmux, creates an empty project root, and asserts successful listing with no leaked glob.
  • Compared master...bug/tmux-go-session-empty-project-root: 2 commits ahead, 0 behind; only .local/bin/tmux-go-session and the focused regression test changed.

Commands not run in this connector-only runtime:

bash tests/tmux-go-session-empty-project-root.test.sh
bash -n .local/bin/tmux-go-session tests/tmux-go-session-empty-project-root.test.sh
shellcheck .local/bin/tmux-go-session tests/tmux-go-session-empty-project-root.test.sh

No command output is claimed for those checks.

Scope

Intentionally not changed:

  • project-root parsing through PROJ_DIR,
  • session sorting or filtering,
  • project-name collision handling,
  • built-in ~/dotfiles and ~/.claude entries,
  • picker behavior, previews, session creation, or session killing,
  • global shell glob settings,
  • unrelated scripts or configuration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant