AI disclosure: This post was written with assistance from an AI (ChatGPT).
Privacy note: All user-specific paths have been generalized (use$HOMEinstead of real usernames). Avoid posting machine-specific secrets from your shell files.
TL;DR
Docker completions worked in my interactive shell, but Docker Desktop kept showing “Unable to find Docker zsh completion in your FPATH.”
The fix was to make the completions available in a path that non-interactive shells can see, and ensure that path is in FPATH very early (via ~/.zshenv).
Quick fix (Option A—what solved it for me):
# 1) Put completion files where Docker expects
mkdir -p "$HOME/.docker/completions"
docker completion zsh > "$HOME/.docker/completions/_docker"
docker compose completion zsh > "$HOME/.docker/completions/_docker-compose" 2>/dev/null || true
# 2) Make FPATH visible to non-interactive shells (put in ~/.zshenv)
# ~/.zshenv  (keep this file minimal—no heavy logic)
export FPATH="$HOME/.docker/completions:/opt/homebrew/share/zsh/site-functions:${FPATH:-}"
# 3) Refresh completion cache and restart shell
command rm -f "$HOME"/.zcompdump* 2>/dev/null
exec zsh -l
Alternative (Option B—UI-friendly too): also install to Homebrew’s site-functions:
BREW_PREFIX="$(brew --prefix)"
mkdir -p "$BREW_PREFIX/share/zsh/site-functions"
docker completion zsh > "$BREW_PREFIX/share/zsh/site-functions/_docker"
docker compose completion zsh > "$BREW_PREFIX/share/zsh/site-functions/_docker-compose" 2>/dev/null || true
chmod -R go-w "$BREW_PREFIX/share/zsh"
command rm -f "$HOME"/.zcompdump* 2>/dev/null
exec zsh -l
Either approach makes Docker Desktop happy and keeps completions working.
Symptoms
- In Terminal/iTerm, pressing TAB on dockerordocker composeworked.
- But Docker Desktop → Settings → General → “Configure shell completions” (or similar) kept showing: “Unable to find Docker zsh completion in your FPATH.”
What’s actually going on
- 
Interactive shells (your usual Terminal) read ~/.zshrc, runoh-my-zsh,compinit, etc. Completions can be loaded from places like~/.oh-my-zsh/cache/completions/_docker.
- 
Non-interactive shells (like checks run by apps using /bin/zsh -lc '...') often do not read~/.zshrc. They usually read only~/.zshenv. If yourFPATHis only set in~/.zshrc, that non-interactive check won’t see the_dockerfile—hence the warning.
Bottom line: Docker Desktop’s detection is conservative. If _docker isn’t visible to a non-interactive zsh, you’ll get that message—even if completions work fine interactively.
The fix I used (Option A)
- Create the completion files where Docker commonly looks:
mkdir -p "$HOME/.docker/completions"
docker completion zsh > "$HOME/.docker/completions/_docker"
docker compose completion zsh > "$HOME/.docker/completions/_docker-compose" 2>/dev/null || true
If you still use the legacy
docker-composebinary:command -v docker-compose >/dev/null && \ docker-compose completion zsh > "$HOME/.docker/completions/_docker-compose"
- 
Expose the path to non-interactive shells by editing ~/.zshenv:
# ~/.zshenv  (keep this tiny—env only)
export FPATH="$HOME/.docker/completions:/opt/homebrew/share/zsh/site-functions:${FPATH:-}"
⚠️
~/.zshenvis read by every zsh invocation. Don’t put heavy logic or commands here—only lightweight environment setup.
- Rebuild completion cache and restart a login shell:
command rm -f "$HOME"/.zcompdump* 2>/dev/null
exec zsh -l
- Verify:
# Show every candidate file named _docker across fpath
print -rl -- $^fpath/_docker(.N)
# After triggering a completion at least once:
whence -v _docker   # should print “... from .../_docker”
Alternative (Option B): install into Homebrew site-functions
Many setups include $(brew --prefix)/share/zsh/site-functions in fpath by default. Placing _docker there tends to satisfy both interactive and non-interactive checks:
BREW_PREFIX="$(brew --prefix)"
mkdir -p "$BREW_PREFIX/share/zsh/site-functions"
docker completion zsh > "$BREW_PREFIX/share/zsh/site-functions/_docker"
docker compose completion zsh > "$BREW_PREFIX/share/zsh/site-functions/_docker-compose" 2>/dev/null || true
chmod -R go-w "$BREW_PREFIX/share/zsh"
command rm -f "$HOME"/.zcompdump* 2>/dev/null
exec zsh -l
Sanity checks & useful one-liners
# Where will zsh look for the _docker file?
print -rl -- $^fpath/_docker(.N)
# After you hit TAB on `docker`, what file is actually sourced?
whence -v _docker
# (If you’re curious) emulate a non-interactive check like apps do:
# Note: this won’t run compinit unless you call it.
# 1) Just check files exist in $fpath (no compinit):
/bin/zsh -lc 'print -rl -- $^fpath/_docker(.N) || echo "no _docker in fpath"'
# 2) Or explicitly run compinit first, then ask whence:
 /bin/zsh -lc 'autoload -Uz compinit; compinit -i; whence -v _docker || echo NO__docker'
Gotchas I hit (and how to avoid them)
- grep≠- rgflags: If you alias- grep='rg', note that- rg -Emeans “encoding,” not “extended regex.”
 Use- egrep='rg'and- fgrep='rg -F'.
- Safety aliases vs real - rm: If you alias- rm='trash', commands like- rm -fwill fail.
 Use- command rmor- \rmwhen you truly want real- rm.
- forloop syntax in zsh: Don’t forget- do … done.
 
  for d in $fpath; do [[ -r $d/_docker ]] && echo "$d/_docker"; done
- 
Load order: It’s conventional to source zsh-syntax-highlightinglast to avoid clobbering by later plugins.
Appendix: Which zsh files run when?
- 
~/.zshenv— read by all invocations (interactive, non-interactive, login, non-login). Keep it tiny and safe.
- 
~/.zprofile— read by login shells.
- 
~/.zshrc— read by interactive shells (your usual Terminal tabs).
- 
~/.zlogin— after~/.zprofilefor login shells.
Docker Desktop (or other apps) often check using /bin/zsh -lc '...', which does not read your ~/.zshrc. Hence putting FPATH in ~/.zshenv ensures the completion files are discoverable in that scenario.
Final notes
- If everything completes on TAB, you’re functionally fine—even if some UI still looks grumpy.
- For posting config snippets on the internet, redact usernames and avoid sharing secrets. Prefer $HOMEand$BREW_PREFIXin examples.
If this helped you, someone else wrestling with zsh and Docker Desktop will probably appreciate it too. Happy hacking! 🐳🧰
 
 
    
Top comments (0)