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) {
				continue

View on GitHub ↗ (pinned to 8a468df065)

Solutions

  1. Check the reported path — it exists but is a file; point the configuration/command at its parent directory or the intended directory.
  2. If a symlink is involved, ensure it targets a directory.
  3. 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

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


AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31). Data as JSON: /data/errors/11ad8583d7798002.json. Report an issue: GitHub ↗.