twpayne/chezmoi · error

%s: not allowed in %s directory

Error message

%s: not allowed in %s directory

What it means

Files inside the .chezmoiexternal source directory whose names begin with chezmoi's reserved Prefix (".") are not permitted. The walk over the externals directory stat()s each entry and rejects entries named like internal chezmoi files, since they could be confused with chezmoi control files.

Source

Thrown at internal/chezmoi/sourcestate.go:1490

		s.externals[targetRelPath] = append(s.externals[targetRelPath], &external)
	}
	return nil
}

// addExternalDir adds all externals in externalsDirAbsPath to s.
func (s *SourceState) addExternalDir(ctx context.Context, externalsDirAbsPath AbsPath) error {
	walkFunc := func(ctx context.Context, externalAbsPath AbsPath, fileInfo fs.FileInfo, err error) error {
		if externalAbsPath == externalsDirAbsPath {
			return nil
		}
		if err == nil && fileInfo.Mode().Type() == fs.ModeSymlink {
			fileInfo, err = s.system.Stat(externalAbsPath)
		}
		switch {
		case err != nil:
			return err
		case strings.HasPrefix(fileInfo.Name(), Prefix):
			return fmt.Errorf("%s: not allowed in %s directory", externalAbsPath, externalsDirName)
		case strings.HasPrefix(fileInfo.Name(), ignorePrefix):
			if fileInfo.IsDir() {
				return fs.SkipDir
			}
			return nil
		case fileInfo.Mode().IsRegular():
			parentAbsPath, _ := externalAbsPath.Split()
			return s.addExternal(externalAbsPath, parentAbsPath.Dir())
		case fileInfo.IsDir():
			return nil
		default:
			return &UnsupportedFileTypeError{
				absPath: externalAbsPath,
				mode:    fileInfo.Mode(),
			}
		}
	}
	return concurrentWalkSourceDir(ctx, s.system, externalsDirAbsPath, walkFunc)

View on GitHub (pinned to f901167e46)

Solutions

  1. Remove or rename the dot-prefixed file inside the .chezmoiexternal.d directory
  2. Add the file to .gitignore so it never enters the source state
  3. If it must be tracked, rename it without a leading dot

Example fix

# before
.chezmoiexternal.d/
  .DS_Store
  browser.toml
# after (delete .DS_Store)
.chezmoiexternal.d/
  browser.toml
Defensive patterns

Strategy: validation

Validate before calling

for _, f := range entriesOfExternalsDir {
    if strings.HasPrefix(f, ".") {
        fmt.Printf("remove dot-prefixed file %s from .chezmoiexternal.d\n", f)
    }
}

Prevention

When it happens

Trigger: Placing a file or directory whose name starts with "." (the Prefix) inside the .chezmoiexternal.d directory while chezmoi walks the externals directory (sourcestate.go:1490).

Common situations: Hidden files (e.g. .DS_Store, .gitignore) extracted or committed inside .chezmoiexternal.d, or accidentally copying dotfiles into the externals directory.

Related errors


AI-assisted analysis of twpayne/chezmoi@f901167e46 (2026-09-01). Data as JSON: /api/errors/c356c62dd94053ed. Report an issue: GitHub.