gohugoio/hugo · error

failed to vendor module

Error message

failed to vendor module: %w

What it means

Returned by Client.Vendor when c.fs.Stat(sourceFilename) fails for a mount's source path inside the module directory. sourceFilename is filepath.Join(dir, mount.Source). The error is wrapped with %w so the underlying fs error (usually 'file does not exist') is unwrappable. This guards against declared mounts pointing at files/dirs that are not actually present in the dependency.

Solutions

  1. Check the wrapped error for the exact missing path, then verify that path exists in the module version you are pulling.
  2. Correct mount.Source in the module config to match the real directory/file name.
  3. Bump or pin the module to a version that contains the expected files.
  4. Remove mounts for paths you do not actually need.

Example fix

# before
[[module.mounts]]
source = "i18n"   # directory no longer exists upstream
target = "i18n"

# after
[[module.mounts]]
source = "locales"
target = "i18n"
Defensive patterns

Strategy: validation

Validate before calling

// Verify each mount source exists before vending.
func mountsExist(fs afero.Fs, dir string, mounts []Mount) error {
    for _, mnt := range mounts {
        if _, err := fs.Stat(filepath.Join(dir, mnt.Source)); err != nil {
            return fmt.Errorf("mount source missing: %s: %w", mnt.Source, err)
        }
    }
    return nil
}

Try / catch

if err := client.Vendor(); err != nil {
    if errors.Is(err, os.ErrNotExist) || strings.Contains(err.Error(), "failed to vendor module") {
        // re-download module or fix mount.Source
    }
}

Prevention

When it happens

Trigger: Running `hugo mod vendor` when a module's [[module.mounts]] source path does not exist on disk relative to the module's Dir(). Common when the mount source was renamed/removed in a newer module version but the consuming project pins an older config, or when a mount points to a path only present on a specific platform.

Common situations: Module author removed a directory (e.g. 'i18n') but a mount still references it; typo in mount.Source; a module pinned to a version that predates a directory the consuming project expects; vendoring after a partial git checkout of a module.

Related errors


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

Appendix: source

Thrown at modules/client.go:241

			continue
		}

		// See https://github.com/gohugoio/hugo/issues/8239
		// This is an error situation. We need something to vendor.
		if t.Mounts() == nil {
			return fmt.Errorf("cannot vendor module %q, need at least one mount", t.PathVersionQuery(false))
		}

		fmt.Fprintln(&modulesContent, "# "+t.PathVersionQuery(true)+" "+t.Version())

		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)
				}
			}
		}

View on GitHub (pinned to 52c9bd7908)