Debugging zsh-autocomplete in tmux
The Problem
In tmux, I was annoyed by zsh-autocomplete, which displayed a search history. Once I was on a line I wanted, but needed to make modification, I attempt to use the left arrow key, which ended up moving me up the history menu. I had an idea that Claude would be able to help me debug this, but first, I discovered there was a discrepancy between tmux and non-tmux environments.
When I started a new shell in Terminal and press the up arrow, I got the expected search history behavior, but when I started a new tmux session and did the same, I got the default zsh history behavior.
To start with, the keybindings were different.
# Outside tmux
$ bindkey '^[[A'
"^[[A" up-line-or-search # ✓ (zsh-autocomplete working)
# Inside tmux
$ bindkey '^[[A'
"^[[A" up-line-or-history # ✗ (default zsh, plugin not working)
The plugin was loaded by znap, worked when manually sourced, but mysteriously failed only in tmux sessions.
Narrowing the problems down
Hypothesis: Powerlevel10k Instant Prompt Interference
Claude hypothesized Powerlevel10k’s instant prompt might be interfering with plugin initialization in tmux.
Test: Modified ~/.zshrc to skip p10k instant prompt in tmux:
if [[ -z "$TMUX" ]] && [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then
source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh"
fi
Result: ✗ Still broken. The instant prompt wasn’t the culprit.
Hypothesis: Tmux Shell Initialization
Claude hypothesized that Tmux might not be spawning properly interactive shells.
Test: Added to tmux.conf:
set -g default-command "${SHELL}"
Result: ✗ Still broken. Shell initialization was fine.
Hypothesis: Compiled Files (.zwc) Corruption
Claude hypothsized Zsh’s compiled bytecode files might have stale or environment-specific code.
Discovery: Found numerous .zwc files in the plugin directory.
Test: Deleted all compiled files:
find ~/.files/zsh.d/Repos/marlonrichert/zsh-autocomplete -name "*.zwc" -delete
rm ~/.zshrc.zwc
Result: ✗ Made it worse. In fact, we discovered the behaviors were reversed. Tmux was loading the search history, but zsh was loading the default history behavior. While pondering on this, Claude checked if the plugin was out of date.
$ cd ~/.files/zsh.d/Repos/marlonrichert/zsh-autocomplete
$ git status
Your branch is behind 'origin/main' by 8 commits
We updated the plugin. No dice.
I asked Claude to consider searching online for similar issues. It agreed.
Searching GitHub Issues
Claude searched for similar problems and found GitHub issue #365: “Arrow Keys not working with default settings”
Key finding from the maintainer:
“The problem is somewhere in your dotfiles and not in
zsh-autocomplete. Sourcing zsh-autocomplete twice resolved the issue, indicating that something else in their configuration was overriding the plugin’s keybindings.”
Recommended solution: Comment out lines from .zshrc until you find the one that causes the problem.
Systematic Debugging
We created diagnostic scripts to isolate the problem:
# Test if plugin actually loads
znap status marlonrichert/zsh-autocomplete # ✓ Shows as loaded
# Test if functions are defined
typeset -f _autocomplete__async_context_menu # ✗ Not defined
# Test direct sourcing
source ~/.files/zsh.d/Repos/marlonrichert/zsh-autocomplete/zsh-autocomplete.plugin.zsh
bindkey '^[[A' # ✓ Shows "up-line-or-search" - It works when sourced directly (existing behavior)
Claude came to conclude that something after loading znap was resetting the keybindings.
Binary Search Through Config
Started testing sections of the config:
# Test 1: Just portable config
zsh -c 'source ~/.files/zsh.d/zshrc && bindkey "^[[A"'
# Result: up-line-or-history ✗
# Test 2: Plugins only
zsh -c 'znap source marlonrichert/zsh-autocomplete && bindkey "^[[A"'
# Result: up-line-or-search ✓
# Test 3: Plugins + one option at a time...
Finding the issue
Claude tested if setopt emacs was the culprit:
zsh -c 'znap source marlonrichert/zsh-autocomplete && \
echo "After plugin:" && bindkey "^[[A" && \
setopt emacs && \
echo "After setopt emacs:" && bindkey "^[[A"'
Output:
After plugin:
"^[[A" up-line-or-search
After setopt emacs:
"^[[A" up-line-or-history # There it is.
Root Cause Analysis
In ~/.files/zsh.d/zshrc, the configuration order was:
# Line 17-21: Load plugins
znap source zsh-users/zsh-autosuggestions
znap source zsh-users/zsh-syntax-highlighting
znap source marlonrichert/zsh-autocomplete
# ... 30 lines later ...
# Line 51: setopt emacs resets keybindings
setopt emacs # ← Resets keybindings
Why it seemed tmux-specific:
It was actually broken everywhere, but we had stale compiled .zwc files that were preserving the old behavior in some sessions. Once we cleared those, the bug became obvious.
The Solution
All setops should be set before loading plugins.
# Set all shell options BEFORE loading plugins
setopt emacs # use emacs keybindings
setopt no_nomatch # avoid zsh: no matches found: foo
setopt longlistjobs # list jobs with PIDs
setopt pushdignoredups # don't push duplicates to the directory stack
setopt pushdminus # flip the meaning of + and - when using pushd/popd
setopt interactive_comments # Allow comments even in interactive shells
setopt extendedglob # enable extended globbing
setopt nocaseglob # case-insensitive globbing
# NOW load plugins - they can override the defaults above
znap source zsh-users/zsh-autosuggestions
znap source zsh-users/zsh-syntax-highlighting
znap source marlonrichert/zsh-autocomplete
Now, it was back to the original problem. I needed the left/right arrow keys to allow editing the history line I was on.
# Customize zsh-autocomplete keybindings
# Enter accepts without executing
bindkey -M menuselect '\r' .accept-line
# Make left/right arrows exit menu and allow line editing
bindkey -M menuselect '^[[D' .accept-line # Left arrow
bindkey -M menuselect '^[[C' .accept-line # Right arrow
Now pressing any arrow key in the menu accepts the selection and lets you edit it.
Time spent debugging: ~2 hours Lines of code changed: 10 (moved, not added)
That’s it.