gohugoio/hugo · error

failed to make target dir

Error message

failed to make target dir: %w

What it means

Returned by Client.Vendor when c.fs.MkdirAll(targetDir, 0o755) fails for a file-type mount, before copying the file itself. targetDir is the parent of the vendored file path. Wrapped with %w. The file mount path is filepath.Join(vendorDir, pathVersion, mount.Source).

Solutions

  1. Read the wrapped error for the OS cause (permission, read-only, invalid name).
  2. Ensure the project working dir is writable: `chmod -R u+w .` or remove a stale _vendor dir.
  3. Delete _vendor and re-run `hugo mod vendor` to get a clean target tree.
  4. Avoid mount.Source paths that collide with file/directory names already vendored.

Example fix

# before: stale _vendor from a failed run blocks mkdir
rm -rf _vendor
hugo mod vendor   # still fails due to perms

# after
chmod -R u+w . && rm -rf _vendor
hugo mod vendor
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the _vendor tree is clean/writable before vendoring.
func prepareVendorDir(fs afero.Fs, dir string) error {
    if err := fs.RemoveAll(dir); err != nil {
        return err
    }
    return fs.MkdirAll(dir, 0o755)
}

Try / catch

if err := client.Vendor(); err != nil {
    if strings.Contains(err.Error(), "failed to make target dir") {
        // remove stale _vendor, fix permissions, retry
    }
}

Prevention

When it happens

Trigger: Running `hugo mod vendor` where a mount source is a single file whose target parent directory cannot be created: permission denied, read-only filesystem, invalid path characters, or a path collision with an existing file.

Common situations: Working directory or _vendor is read-only; a previous failed vendor left a file where a directory is now expected; OS path restrictions (Windows reserved names); running Hugo as a user lacking write access to the project.

Related errors


AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09). Data as JSON: /api/errors/e39864ac83245b91. Report an issue: GitHub.

Appendix: source

Thrown at modules/client.go:252

		dir := t.Dir()

		for _, mount := range t.Mounts() {
			sourceFilename := filepath.Join(dir, mount.Source)
			targetFilename := filepath.Join(vendorDir, t.PathVersionQuery(true), mount.Source)
			fi, err := c.fs.Stat(sourceFilename)
			if err != nil {
				return fmt.Errorf("failed to vendor module: %w", err)
			}

			if fi.IsDir() {
				if err := hugio.CopyDir(c.fs, sourceFilename, targetFilename, nil); err != nil {
					return fmt.Errorf("failed to copy module to vendor dir: %w", err)
				}
			} else {
				targetDir := filepath.Dir(targetFilename)

				if err := c.fs.MkdirAll(targetDir, 0o755); err != nil {
					return fmt.Errorf("failed to make target dir: %w", err)
				}

				if err := hugio.CopyFile(c.fs, sourceFilename, targetFilename); err != nil {
					return fmt.Errorf("failed to copy module file to vendor: %w", err)
				}
			}
		}

		// Include the resource cache if present.
		resourcesDir := filepath.Join(dir, files.FolderResources)
		_, err := c.fs.Stat(resourcesDir)
		if err == nil {
			if err := hugio.CopyDir(c.fs, resourcesDir, filepath.Join(vendorDir, t.PathVersionQuery(true), files.FolderResources), nil); err != nil {
				return fmt.Errorf("failed to copy resources to vendor dir: %w", err)
			}
		}

		// Include the config directory if present.

View on GitHub (pinned to 52c9bd7908)