gohugoio/hugo · error

failed to copy config dir to vendor dir

Error message

failed to copy config dir to vendor dir: %w

What it means

Returned by Client.Vendor when hugio.CopyDir fails copying the module's 'config' directory into _vendor, after Stat confirmed it exists. Wrapped with %w. This step mirrors the resources copy but for the module's top-level config folder, so module config travels with the vendored copy.

Solutions

  1. Inspect the wrapped error for the concrete OS failure.
  2. Repair permissions or broken symlinks in the module's config/ directory.
  3. Make _vendor writable and ensure free disk space.
  4. Re-download the module cleanly (`hugo mod clean` then vendor) if the cache is corrupted.

Example fix

# before
hugo mod vendor   # config/ copy fails

# after
hugo mod clean
hugo mod vendor
Defensive patterns

Strategy: try-catch

Validate before calling

// Check the module's config dir is readable before vendoring.
func configDirReadable(fs afero.Fs, dir string) error {
    cd := filepath.Join(dir, "config")
    if _, err := fs.Stat(cd); err == nil {
        f, err := fs.Open(cd)
        if err != nil {
            return err
        }
        f.Close()
    }
    return nil
}

Try / catch

if err := client.Vendor(); err != nil {
    if strings.Contains(err.Error(), "failed to copy config dir to vendor dir") {
        // unwrap to find permission/symlink/space cause, fix, retry
    }
}

Prevention

When it happens

Trigger: Running `hugo mod vendor` for a module that ships a 'config/' directory whose recursive copy fails for filesystem reasons (permissions, symlinks, disk space, read-only target).

Common situations: Module config dir containing platform-specific or symlinked files; restrictive permissions in the module cache; _vendor on a read-only mount; CI ephemeral filesystem limits.

Related errors


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

Appendix: source

Thrown at modules/client.go:275

				}
			}
		}

		// 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.
		configDir := filepath.Join(dir, "config")
		_, err = c.fs.Stat(configDir)
		if err == nil {
			if err := hugio.CopyDir(c.fs, configDir, filepath.Join(vendorDir, t.PathVersionQuery(true), "config"), nil); err != nil {
				return fmt.Errorf("failed to copy config dir to vendor dir: %w", err)
			}
		}

		// Also include any theme.toml or config.* or hugo.* files in the root.
		configFiles, _ := afero.Glob(c.fs, filepath.Join(dir, "config.*"))
		configFiles2, _ := afero.Glob(c.fs, filepath.Join(dir, "hugo.*"))
		configFiles = append(configFiles, configFiles2...)
		configFiles = append(configFiles, filepath.Join(dir, "theme.toml"))
		for _, configFile := range configFiles {
			if err := hugio.CopyFile(c.fs, configFile, filepath.Join(vendorDir, t.PathVersionQuery(true), filepath.Base(configFile))); err != nil {
				if !herrors.IsNotExist(err) {
					return err
				}
			}
		}
	}

	if modulesContent.Len() > 0 {

View on GitHub (pinned to 52c9bd7908)