DEV Community

Geoffrey Kim
Geoffrey Kim

Posted on

Fixing Docker Desktop’s “Unable to find Docker zsh completion in your FPATH” on macOS (Oh My Zsh + Homebrew)

AI disclosure: This post was written with assistance from an AI (ChatGPT).
Privacy note: All user-specific paths have been generalized (use $HOME instead 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Either approach makes Docker Desktop happy and keeps completions working.


Symptoms

  • In Terminal/iTerm, pressing TAB on docker or docker compose worked.
  • 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, run oh-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 your FPATH is only set in ~/.zshrc, that non-interactive check won’t see the _docker file—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)

  1. 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
Enter fullscreen mode Exit fullscreen mode

If you still use the legacy docker-compose binary:

command -v docker-compose >/dev/null && \
  docker-compose completion zsh > "$HOME/.docker/completions/_docker-compose"
  1. 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:-}"
Enter fullscreen mode Exit fullscreen mode

⚠️ ~/.zshenv is read by every zsh invocation. Don’t put heavy logic or commands here—only lightweight environment setup.

  1. Rebuild completion cache and restart a login shell:
command rm -f "$HOME"/.zcompdump* 2>/dev/null
exec zsh -l
Enter fullscreen mode Exit fullscreen mode
  1. 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”
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

Gotchas I hit (and how to avoid them)

  • greprg flags: If you alias grep='rg', note that rg -E means “encoding,” not “extended regex.”
    Use egrep='rg' and fgrep='rg -F'.

  • Safety aliases vs real rm: If you alias rm='trash', commands like rm -f will fail.
    Use command rm or \rm when you truly want real rm.

  • for loop syntax in zsh: Don’t forget do … done.

  for d in $fpath; do [[ -r $d/_docker ]] && echo "$d/_docker"; done
Enter fullscreen mode Exit fullscreen mode
  • Load order: It’s conventional to source zsh-syntax-highlighting last 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 ~/.zprofile for 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 $HOME and $BREW_PREFIX in examples.

If this helped you, someone else wrestling with zsh and Docker Desktop will probably appreciate it too. Happy hacking! 🐳🧰

Top comments (0)