twpayne/chezmoi · error
%s: %w
Error message
%s: %w
What it means
When reading a .chezmoiexternal file (or external entry source), chezmoi first executes it as a template, then wraps any template execution error with the source file path for context. The underlying %w error is the template failure (syntax error, missing variable, failing function).
Source
Thrown at internal/chezmoi/sourcestate.go:1441
// addExternal adds external source entries to s.
func (s *SourceState) addExternal(sourceAbsPath, parentAbsPath AbsPath) error {
parentRelPath, err := parentAbsPath.TrimDirPrefix(s.sourceDirAbsPath)
if err != nil {
return err
}
parentSourceRelPath := NewSourceRelDirPath(parentRelPath.String())
parentTargetSourceRelPath, err := parentSourceRelPath.TargetRelPath(s.encryption.EncryptedSuffix())
if err != nil {
return err
}
format, err := FormatFromAbsPath(sourceAbsPath.TrimSuffix(TemplateSuffix))
if err != nil {
return err
}
data, err := s.executeTemplate(sourceAbsPath)
if err != nil {
return fmt.Errorf("%s: %w", sourceAbsPath, err)
}
externals := make(map[string]External)
if err := format.Unmarshal(data, &externals); err != nil {
return fmt.Errorf("%s: %w", sourceAbsPath, err)
}
s.mutex.Lock()
defer s.mutex.Unlock()
for targetPath, external := range externals {
if external.TargetPath != "" {
targetPath = external.TargetPath
}
if targetPath == "" {
return fmt.Errorf("%s: empty path", sourceAbsPath)
}
externalPath := path.Clean(targetPath)
if strings.HasPrefix(externalPath, "/") || filepath.IsAbs(externalPath) {
return fmt.Errorf("%s: %s: path is not relative", sourceAbsPath, targetPath)View on GitHub (pinned to f901167e46)
Solutions
- Run 'chezmoi execute-template < <file>' or inspect the wrapped error message to find the failing template expression and fix it.
- Remove unnecessary {{ }} constructs from the external file (static content doesn't need templating).
- Verify template variables exist for your environment ('chezmoi data').
Example fix
// before (.chezmoiexternal.toml.tmpl)
url = "https://example.com/{{ .undefinedVar }}"
// after
url = "https://example.com/{{ .chezmoi.hostname }}/asset" Defensive patterns
Strategy: try-catch
Try / catch
err := sourceState.readExternal(...)
var pathErr *fmt.WrapError // or match on prefix
if err != nil {
if strings.Contains(err.Error(), ": ") && templateFailed(err) {
// log file path from prefix, surface inner template error to user
}
} Prevention
- Test external templates with 'chezmoi execute-template'
- Only template files that need dynamic values
- Pin template functions you rely on to chezmoi versions
When it happens
Trigger: A .chezmoiexternal.toml/.json/.yaml containing template syntax ({{ ... }}) that fails to execute, e.g. referencing undefined .chezmoi* variables or calling a template function with bad args.
Common situations: Template typos like {{ .chezmoi.hostname }} in files that don't get template context; copy-pasted template snippets into externals; version change removing a template function.
Related errors
AI-assisted analysis of twpayne/chezmoi@f901167e46 (2026-09-01).
Data as JSON: /api/errors/b81eeb95b92dbf6a.
Report an issue: GitHub.