openclaw - ✅(Solved) Fix [Bug]: hell completion writes to wrong profile path when ZDOTDIR or XDG_CONFIG_HOME is set [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#63069Fetched 2026-04-09 07:58:57
View on GitHub
Comments
0
Participants
1
Timeline
3
Reactions
0
Author
Participants
Timeline (top)
labeled ×2cross-referenced ×1

Shell completion install writes to hardcoded ~/.zshrc, ~/.config/fish/config.fish, etc. instead of respecting $ZDOTDIR, $XDG_CONFIG_HOME, and bash .bash_profile fallback, causing the completion source line to be written to a file the shell never reads.

Root Cause

Shell completion install writes to hardcoded ~/.zshrc, ~/.config/fish/config.fish, etc. instead of respecting $ZDOTDIR, $XDG_CONFIG_HOME, and bash .bash_profile fallback, causing the completion source line to be written to a file the shell never reads.

Fix Action

Fixed

PR fix notes

PR #63082: fix(cli): respect ZDOTDIR and XDG_CONFIG_HOME in shell completion pro…

Description (problem / solution / changelog)

Summary

  • Problem: Shell completion install/check hardcodes ~/.zshrc, ~/.config/fish/config.fish, etc. and ignores $ZDOTDIR, $XDG_CONFIG_HOME, and bash .bash_profile fallback. This causes completion to be written to a file the shell never reads, silently breaking completion for users with non-default shell config directories.
  • Why it matters: Users who organize their dotfiles under $ZDOTDIR or $XDG_CONFIG_HOME (a common and well-documented practice) get broken completion on every install/update, and may get an orphan ~/.zshrc created unexpectedly.
  • What changed: Unified all profile path resolution into a single getShellProfilePath() function that respects $ZDOTDIR (zsh), $XDG_CONFIG_HOME (fish, PowerShell on non-Windows), and .bashrc/.bash_profile fallback (bash). Removed duplicated path logic from installCompletion(). Updated doctor-completion.ts to use the resolved path in user-facing messages.
  • What did NOT change (scope boundary): Completion script generation logic is untouched. No changes to how the cache is generated or where it is stored. PowerShell automated install remains unsupported (was already gated).

Change Type (select all)

  • Bug fix

Scope (select all touched areas)

  • UI / DX

Linked Issue/PR

  • Closes #63069

Root Cause (if applicable)

  • Root cause: getShellProfilePath() and installCompletion() independently hardcoded default profile paths ($HOME/.zshrc, $HOME/.config/fish/config.fish) without consulting the shell-specific environment variables that control where dotfiles are actually read from.
  • Missing detection / guardrail: No tests existed for non-default profile path resolution. The two functions that computed profile paths (getShellProfilePath for checking, inline logic in installCompletion for writing) were never compared or unified.
  • Contributing context: The bash path had an additional inconsistency: getShellProfilePath() returned only ~/.bashrc, while installCompletion() had a .bash_profile fallback — causing check/install to disagree.

Regression Test Plan (if applicable)

For bug fixes or regressions, name the smallest reliable test coverage that should catch this. Otherwise write N/A.

  • Coverage level that should have caught this:
    • Unit test
  • Target test or file: src/cli/completion-cli.test.ts
  • Scenario the test should lock in:
    • getShellProfilePath("zsh", { ZDOTDIR: "/custom" }) returns /custom/.zshrc
    • getShellProfilePath("fish", { XDG_CONFIG_HOME: "/custom" }) returns /custom/fish/config.fish
    • getShellProfilePath("bash", ...) returns .bash_profile when .bashrc does not exist
    • getShellProfilePath("powershell", { XDG_CONFIG_HOME: "/custom" }) returns /custom/powershell/... on non-Windows
  • Why this is the smallest reliable guardrail: Pure function with env injection — no I/O mocks needed for zsh/fish/powershell; bash uses temp dirs to test filesystem fallback.

User-visible / Behavior Changes

  • Shell completion now correctly installs into ${ZDOTDIR:-$HOME}/.zshrc instead of always ~/.zshrc.
  • Shell completion now correctly installs into ${XDG_CONFIG_HOME:-~/.config}/fish/config.fish instead of always ~/.config/fish/config.fish.
  • Bash completion check and install now consistently prefer .bashrc, falling back to .bash_profile if .bashrc doesn't exist.
  • openclaw doctor and openclaw update "source" hints now show the actual resolved profile path instead of a hardcoded ~/.zshrc/~/.bashrc.

Diagram (if applicable)

N/A.

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No

Repro + Verification

Environment

  • OS: macOS 15.4

Steps

  1. Set ZDOTDIR=/tmp/test-zdotdir and create /tmp/test-zdotdir/.zshrc.
  2. Run openclaw completion --install --shell zsh.
  3. Verify the source line is written to /tmp/test-zdotdir/.zshrc, not ~/.zshrc.

Expected

  • Source line appears in /tmp/test-zdotdir/.zshrc.

Actual

  • Source line written to ~/.zshrc; /tmp/test-zdotdir/.zshrc` untouched.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • 7 new unit tests in src/cli/completion-cli.test.ts covering all four shells with env var overrides.

Human Verification (required)

  • Verified scenarios: zsh with ZDOTDIR, fish with XDG_CONFIG_HOME, bash with only .bash_profile, PowerShell with XDG_CONFIG_HOME
  • Edge cases checked: neither .bashrc nor .bash_profile exist (defaults to .bashrc), empty ZDOTDIR (falls back to $HOME)
  • What I did not verify: Windows PowerShell path (no Windows environment available), live openclaw update end-to-end flow
  • getShellProfilePath("bash", ...) returns .bash_profile when neither .bashrc nor .bash_profile exists (login-shell safety for macOS)

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No
    • users who previously had broken completion will get it working on next openclaw update or openclaw completion --install.

Risks and Mitigations

  • Risk: existsSync in getShellProfilePath() introduces a synchronous filesystem call for the bash path.- Mitigation: Only called for bash shell (not zsh/fish/powershell which use pure env lookups). The function was already used synchronously by callers. The check is a single stat on two well-known paths in $HOME.

Changed files

  • src/cli/completion-cli.test.ts (modified, +78/-1)
  • src/cli/completion-cli.ts (modified, +42/-27)
  • src/commands/doctor-completion.ts (modified, +5/-2)
RAW_BUFFERClick to expand / collapse

Bug type

Behavior bug (incorrect output/state without crash)

Beta release blocker

No

Summary

Shell completion install writes to hardcoded ~/.zshrc, ~/.config/fish/config.fish, etc. instead of respecting $ZDOTDIR, $XDG_CONFIG_HOME, and bash .bash_profile fallback, causing the completion source line to be written to a file the shell never reads.

Steps to reproduce

  1. Set ZDOTDIR=~/.config/zsh in your environment (e.g. via /etc/zshenv or .zshenv), so your actual zsh config lives at ~/.config/zsh/.zshrc.
  2. Run openclaw update or openclaw completion --install.
  3. When prompted "Enable zsh shell completion for openclaw?", select Yes.
  4. Observe that the tool creates/writes to ~/.zshrc instead of ~/.config/zsh/.zshrc.
  5. Open a new shell — completion does not work. The same class of bug applies to:
  • fish users with XDG_CONFIG_HOME set to a non-default path.
  • bash users who only have ~/.bash_profile (common on macOS) — isCompletionInstalled() only checks ~/.bashrc while installCompletion() falls back to ~/.bash_profile, causing a check/install mismatch on every update cycle.

Expected behavior

Completion should be written to the profile file the shell actually loads: ${ZDOTDIR:-$HOME}/.zshrc for zsh, ${XDG_CONFIG_HOME:-~/.config}/fish/config.fish for fish, and consistent .bashrc/.bash_profile fallback for bash.

Actual behavior

  • Completion source line is written to ~/.zshrc regardless of $ZDOTDIR — has no effect if the user's shell reads from a different directory.
  • If ~/.zshrc does not exist, an empty one is created, which may conflict with the user's setup.
  • For bash, isCompletionInstalled() checks ~/.bashrc only, while installCompletion() falls back to ~/.bash_profile — the two functions disagree, leading to repeated "Enable completion?" prompts on every openclaw update.
  • For fish, XDG_CONFIG_HOME is ignored entirely.

OpenClaw version

2026.4.8

Operating system

macOS 15.4

Install method

pnpm dev

Model

CLI-only issue

Provider / routing chain

N/A

Additional provider/model setup details

No response

Logs, screenshots, and evidence

No response

Impact and severity

No response

Additional information

No response

extent analysis

TL;DR

The issue can be fixed by modifying the completion install script to respect environment variables like $ZDOTDIR and $XDG_CONFIG_HOME when writing to shell configuration files.

Guidance

  • Verify that the completion install script is using hardcoded paths instead of environment variables by checking the script's code.
  • Modify the script to use the correct paths for each shell, such as ${ZDOTDIR:-$HOME}/.zshrc for zsh and ${XDG_CONFIG_HOME:-~/.config}/fish/config.fish for fish.
  • Update the isCompletionInstalled() function for bash to check both ~/.bashrc and ~/.bash_profile to ensure consistency with the installCompletion() function.
  • Test the changes by running the completion install script and verifying that the completion source line is written to the correct file.

Example

# Example of how to modify the completion install script for zsh
if [ -n "$ZDOTDIR" ]; then
  zsh_config_file="$ZDOTDIR/.zshrc"
else
  zsh_config_file="$HOME/.zshrc"
fi
echo "source <completion_source>" >> "$zsh_config_file"

Notes

The fix requires modifying the completion install script to respect environment variables and use the correct paths for each shell. This may involve updating the script to handle different shell configurations and environment variables.

Recommendation

Apply workaround: Modify the completion install script to respect environment variables and use the correct paths for each shell, as this will fix the issue without requiring an upgrade to a new version.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

Completion should be written to the profile file the shell actually loads: ${ZDOTDIR:-$HOME}/.zshrc for zsh, ${XDG_CONFIG_HOME:-~/.config}/fish/config.fish for fish, and consistent .bashrc/.bash_profile fallback for bash.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING