gohugoio/hugo · error
is not a directory
Error message
%q is not a directory
What it means
Returned by hugio.CopyDir when the source path exists but fs.Stat reports it as a file rather than a directory. CopyDir walks entries via ReadDir, so a non-directory source is an unrecoverable precondition violation.
Solutions
- Point `from` at an actual directory.
- If only a single file needs copying, use hugio.CopyFile instead.
- Resolve symlinks before calling and verify os.Stat.IsDir().
Example fix
// before hugio.CopyDir(fs, "content/post.md", "public/post.md", nil) // after hugio.CopyFile(fs, "content/post.md", "public/post.md")
Defensive patterns
Strategy: validation
Validate before calling
func ensureDir(fs afero.Fs, p string) error {
info, err := fs.Stat(p)
if err != nil { return err }
if !info.IsDir() { return fmt.Errorf("%q is not a directory", p) }
return nil
} Prevention
- Verify fs.Stat(...).IsDir() before CopyDir.
- Use CopyFile for single files.
- Resolve symlinks before stat.
When it happens
Trigger: Calling hugio.CopyDir(fs, from, to, shouldCopy) where `from` resolves to a regular file, a symlink to a file, or a special file.
Common situations: Theme/static-file copying during `hugo` builds when a configured directory (e.g. `staticDir`, `assetDir`) actually points at a single file; or recursive walks where a symlink unexpectedly resolves to a file.
Related errors
- failed to copy config dir to vendor dir
- failed to copy module file to vendor
- failed to copy module to vendor dir
- failed to copy to
- failed to copy resources to vendor dir
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/11ad8583d7798002.
Report an issue: GitHub.
Appendix: 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 52c9bd7908)