gohugoio/hugo · error
%q is not a directory
Error message
%q is not a directory
What it means
hugio.CopyDir stats the source path and requires it to be a directory before recursively copying entries; if the path exists but is a regular file, it fails fast with this error rather than silently copying a single file. This guards internal copy operations (e.g. hugo new site/theme scaffolding, module and resource copying) on the afero filesystem abstraction.
Source
Thrown at common/hugio/copy.go:61
err = fs.Chmod(to, si.Mode())
if err != nil {
return err
}
}
return nil
}
// CopyDir copies a directory.
func CopyDir(fs afero.Fs, from, to string, shouldCopy func(filename string) bool) error {
fi, err := fs.Stat(from)
if err != nil {
return err
}
if !fi.IsDir() {
return fmt.Errorf("%q is not a directory", from)
}
err = fs.MkdirAll(to, 0o777) // before umask
if err != nil {
return err
}
d, err := fs.Open(from)
if err != nil {
return err
}
entries, _ := d.(iofs.ReadDirFile).ReadDir(-1)
for _, entry := range entries {
fromFilename := filepath.Join(from, entry.Name())
toFilename := filepath.Join(to, entry.Name())
if entry.IsDir() {
if shouldCopy != nil && !shouldCopy(fromFilename) {
continueView on GitHub ↗ (pinned to 8a468df065)
Solutions
- Check the reported path — it exists but is a file; point the configuration/command at its parent directory or the intended directory.
- If a symlink is involved, ensure it targets a directory.
- If the path should be a directory, recreate the expected structure (move the file inside a directory of that name).
Defensive patterns
Strategy: validation
Validate before calling
fi, err := os.Stat(from)
if err != nil {
return err
}
if !fi.IsDir() {
return fmt.Errorf("%q is not a directory", from)
} Type guard
func isDir(fs afero.Fs, path string) bool {
fi, err := fs.Stat(path)
return err == nil && fi.IsDir()
} Try / catch
if err := hugio.CopyDir(fs, from, to, nil); err != nil {
if os.IsNotExist(err) || strings.Contains(err.Error(), "is not a directory") {
// caller passed a file or missing path where a directory was expected
}
return err
} Prevention
- Stat the source path and check IsDir() before calling CopyDir
- Use CopyFile for files and CopyDir for directories — don't guess
- Build paths with filepath.Join from verified roots rather than string concatenation
When it happens
Trigger: Any internal call to hugio.CopyDir(fs, from, to, ...) where `from` resolves to a file, not a directory — e.g. a scaffolding or mount path that points at a file, or an overlay/union filesystem entry that materializes as a file.
Common situations: Misconfigured module mounts or commands where a source path intended as a directory is actually a file (or a symlink to a file), stale paths after restructuring a project, or passing a file path to a Hugo command/API expecting a directory.
Related errors
- failed to copy %q to %q: %w
- failed to read archetype file: %w
- failed to open image for decode: %w
- walk: stat: %s
- walk: Readdir: %w
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/11ad8583d7798002.json.
Report an issue: GitHub ↗.