twpayne/chezmoi · error
%s: missing external type
Error message
%s: missing external type
What it means
Returned when an external entry in the source state has an empty type field. chezmoi's readExternal dispatch switch falls into the "" case because it cannot know how to interpret the entry without a type (archive, file, git-repo, etc.).
Source
Thrown at internal/chezmoi/sourcestate.go:2425
// readExternal reads an external and returns its SourceStateEntries.
func (s *SourceState) readExternal(
ctx context.Context,
externalRelPath RelPath,
parentSourceRelPath SourceRelPath,
external *External,
options *ReadOptions,
) (map[RelPath][]SourceStateEntry, error) {
switch external.Type {
case ExternalTypeArchive:
return s.readExternalArchive(ctx, externalRelPath, parentSourceRelPath, external, options)
case ExternalTypeArchiveFile:
return s.readExternalArchiveFile(ctx, externalRelPath, parentSourceRelPath, external, options)
case ExternalTypeFile:
return s.readExternalFile(ctx, externalRelPath, parentSourceRelPath, external, options)
case ExternalTypeGitRepo:
return nil, nil
case "":
return nil, fmt.Errorf("%s: missing external type", externalRelPath)
default:
return nil, fmt.Errorf("%s: unknown external type: %s", externalRelPath, external.Type)
}
}
// readExternalArchive reads an external archive and returns its
// SourceStateEntries.
func (s *SourceState) readExternalArchive(
ctx context.Context,
externalRelPath RelPath,
parentSourceRelPath SourceRelPath,
external *External,
options *ReadOptions,
) (map[RelPath][]SourceStateEntry, error) {
data, urlStr, format, err := s.readExternalArchiveData(ctx, externalRelPath, external, options)
if err != nil {
return nil, err
}View on GitHub (pinned to f901167e46)
Solutions
- Add the missing type field to the external entry (archive, file, or git-repo)
- Use the executable entry format (type = "archive-file" with executable) if that was intended
- Validate .chezmoiexternal with chezmoi doctor or a dry-run apply
Example fix
// before
[.oh-my-zsh]
url = "https://github.com/ohmyzsh/ohmyzsh/archive/master.tar.gz"
// after
[.oh-my-zsh]
type = "archive"
url = "https://github.com/ohmyzsh/ohmyzsh/archive/master.tar.gz" Defensive patterns
Strategy: validation
Validate before calling
// pre-validate external entries
for name, ext := range externals {
if ext.Type == "" { return fmt.Errorf("external %q is missing type", name) }
} Type guard
func hasType(e External) bool { return e.Type != "" } Prevention
- Always set type explicitly in every external entry
- Validate .chezmoiexternal in CI with a dry-run apply
- Keep entries copied from docs complete, including type
When it happens
Trigger: An entry in .chezmoiexternal defines a url (or path) but omits the 'type' key, and the type cannot be inferred (e.g. no archive format guess possible for the given path/URL).
Common situations: Hand-written external config missing the type line; migration from an older chezmoi format; templating that dropped the type field.
Related errors
AI-assisted analysis of twpayne/chezmoi@f901167e46 (2026-09-01).
Data as JSON: /api/errors/5da1117f4d8e4596.
Report an issue: GitHub.