Skip to content

chezmoi apply can have different template data than chezmoi data/chezmoi execute-template #4940

@mjec

Description

@mjec

Describe the bug

chezmoi data will read .chezmoidata files inside external directories, but chezmoi apply will not.

When SourceState.templateDataOnly is true (e.g. when chezmoi data is used), the walkFunc defined in SourceState.Read will walk through external directories, calling addTemplateData on any .chezmoidata.$FORMAT files in those directories.

Conversely, if templateDataOnly is false, readExternalDir is used to process those files, which does not have special handling .chezmoidata (and other special) files. Then fs.SkipDir is returned, which prevents further recursing with walkFunc (which would have special handling for special fiels).

To reproduce

$ chezmoi cd
$ mkdir external_private
$ echo '{"nested": {"sentinel_value": "external data"}}' > external_private/.chezmoidata.json
$ echo '{{ .nested.sentinel_value }}' > ext-data.tmpl
$ chezmoi data | jq '.nested.sentinel_value'
"external data"
$ chezmoi execute-template "$(cat ext-data.tmpl)"; printf "\n"
external data
$ chezmoi diff > /dev/null  # redirection not necessary, just suppressing other diff output
chezmoi: ext-data: template: ext-data.tmpl:1:10: executing "ext-data.tmpl" at <.nested.sentinel_value>: map has no entry for key "nested"
$ 

Expected behavior

The template data available to each command should be the same.

I'd personally prefer if external .chezmoidata files were processed, but either way consistency is what matters, to avoid issues debugging (where this inconsistency surprised me).

Output of command with the --verbose flag

No different.

Notable partial output from running chezmoi data --debug:

time=2026-03-04T16:23:12.427-05:00 level=INFO msg=ReadDir component=system name=/Users/mjec/.local/share/chezmoi/external_private
time=2026-03-04T16:23:12.427-05:00 level=INFO msg=ReadDir component=system name=/Users/mjec/.local/share/chezmoi/external_private/.chezmoidata
time=2026-03-04T16:23:12.427-05:00 level=INFO msg=ReadFile component=system name=/Users/mjec/.local/share/chezmoi/external_private/.chezmoidata/claude-plugins.json size=1183 data="{\n  \"llms\": {\n    \"claude\": {\n      \"marketplaces\": {\n        \"e..."

Whereas chezmoi diff --debug does not show anything about external_private.

Output of chezmoi doctor

Details
$ chezmoi doctor
RESULT    CHECK                       MESSAGE
ok        version                     v2.69.4, commit c4c669c9f2f329233a85802014d26fba3c58a4a4, built at 2026-02-11T08:59:37Z, built by Homebrew
ok        latest-version              v2.69.4
ok        os-arch                     darwin/arm64
ok        uname                       Darwin MacBook-Pro-9.local 25.3.0 Darwin Kernel Version 25.3.0: Wed Jan 28 20:53:15 PST 2026; root:xnu-12377.81.4~5/RELEASE_ARM64_T6000 arm64
ok        go-version                  go1.25.7 (gc)
ok        executable                  /opt/homebrew/bin/chezmoi
ok        upgrade-method              brew-upgrade
ok        config-file                 found ~/.config/chezmoi/chezmoi.toml, last modified 2026-03-04T10:22:15-05:00
warning   source-dir                  ~/.local/share/chezmoi is a git working tree (dirty)
warning   suspicious-entries          ~/.local/share/chezmoi/.git/fsmonitor--daemon.ipc [submodule fsmonitor demon ipc entries edited out]
warning   working-tree                ~/.local/share/chezmoi is a git working tree (dirty)
ok        dest-dir                    ~ is a directory
ok        hardlink                    created hardlink from ~/.local/share/chezmoi to /var/folders/4d/h66416yd4y5d5lyg2f5jphzr0000gp/T/
ok        symlink                     created symlink from .new-name to .old-name
ok        umask                       022
ok        cd-command                  found /bin/zsh
ok        cd-args                     /bin/zsh
info      diff-command                not set
ok        edit-command                found /Applications/Visual Studio Code - Insiders.app/Contents/Resources/app/bin/code
ok        edit-args                   code --wait --new-window
ok        git-command                 found /opt/homebrew/bin/git, version 2.53.0
ok        merge-command               found /opt/homebrew/bin/vimdiff
ok        shell-command               found /bin/zsh
ok        shell-args                  /bin/zsh
ok        age-command                 found /opt/homebrew/bin/age, version 1.3.1
info      gpg-command                 gpg not found in $PATH
info      pinentry-command            not set
ok        1password-command           found /opt/homebrew/bin/op, version 2.32.1
info      bitwarden-command           bw not found in $PATH
info      bitwarden-secrets-command   bws not found in $PATH
info      dashlane-command            dcli not found in $PATH
info      doppler-command             doppler not found in $PATH
info      gopass-command              gopass not found in $PATH
info      keepassxc-command           keepassxc-cli not found in $PATH
info      keepassxc-db                not set
info      keeper-command              keeper not found in $PATH
info      lastpass-command            lpass not found in $PATH
info      pass-command                pass not found in $PATH
info      passhole-command            ph not found in $PATH
info      protonpass-command          pass-cli not found in $PATH
info      rbw-command                 rbw not found in $PATH
info      vault-command               vault not found in $PATH
info      secret-command              not set
$ 

Additional context

I also asked about this in a discussion post before I'd narrowed it down to a bug.

I tracked this down using Claude Code, but verified its output by looking at the code, and wrote up this bug report without the aid of an LLM.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions