golang/go · error

module paths beginning with gopkg.in/ must always have a maj

Error message

module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN:
	go mod init %s

What it means

During 'go mod init', the supplied module path starts with 'gopkg.in/' but module.SplitPathVersion could not extract a valid .vN major-version suffix. gopkg.in paths have a special rule: they MUST carry a .vN suffix. The message embeds a suggested path from suggestGopkgIn (which appends .v1 if no major was detected).

Source

Thrown at src/cmd/go/internal/modload/init.go:1269

func checkModulePath(modPath string) {
	if err := module.CheckImportPath(modPath); err != nil {
		if pathErr, ok := err.(*module.InvalidPathError); ok {
			pathErr.Kind = "module"
			// Same as build.IsLocalPath()
			if pathErr.Path == "." || pathErr.Path == ".." ||
				strings.HasPrefix(pathErr.Path, "./") || strings.HasPrefix(pathErr.Path, "../") {
				pathErr.Err = errors.New("is a local import path")
			}
		}
		base.Fatal(err)
	}
	if err := CheckReservedModulePath(modPath); err != nil {
		base.Fatalf(`go: invalid module path %q: `, modPath)
	}
	if _, _, ok := module.SplitPathVersion(modPath); !ok {
		if strings.HasPrefix(modPath, "gopkg.in/") {
			invalidMajorVersionMsg := fmt.Errorf("module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN:\n\tgo mod init %s", suggestGopkgIn(modPath))
			base.Fatalf(`go: invalid module path "%v": %v`, modPath, invalidMajorVersionMsg)
		}
		invalidMajorVersionMsg := fmt.Errorf("major version suffixes must be in the form of /vN and are only allowed for v2 or later:\n\tgo mod init %s", suggestModulePath(modPath))
		base.Fatalf(`go: invalid module path "%v": %v`, modPath, invalidMajorVersionMsg)
	}
}

// fixVersion returns a modfile.VersionFixer implemented using the Query function.
//
// It resolves commit hashes and branch names to versions,
// canonicalizes versions that appeared in early vgo drafts,
// and does nothing for versions that already appear to be canonical.
//
// The VersionFixer sets 'fixed' if it ever returns a non-canonical version.
func fixVersion(ld *Loader, ctx context.Context, fixed *bool) modfile.VersionFixer {
	return func(path, vers string) (resolved string, err error) {
		defer func() {
			if err == nil && resolved != vers {

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Use the suggested command printed in the error, e.g. 'go mod init gopkg.in/user/project.v1'.
  2. Pick the major version explicitly: .v1, .v2, .v3 ... matching the library's actual major.
  3. If you do not need gopkg.in semantics, choose a normal domain path (example.com/project).
  4. Verify the path matches the upstream gopkg.in repository exactly.

Example fix

// before
$ go mod init gopkg.in/yaml
// module paths beginning with gopkg.in/ must always have a major version suffix ...:
//   go mod init gopkg.in/yaml.v1

// after
$ go mod init gopkg.in/yaml.v3
Defensive patterns

Strategy: validation

Validate before calling

// Validate a gopkg.in path before 'go mod init'.
func validGopkgIn(p string) error {
    if !strings.HasPrefix(p, "gopkg.in/") { return nil }
    if _, _, ok := module.SplitPathVersion(p); !ok {
        return fmt.Errorf("gopkg.in path needs .vN suffix, e.g. %s.v1", strings.TrimRight(p, "/"))
    }
    return nil
}

Type guard

func isGopkgInWithMajor(p string) bool {
    return strings.HasPrefix(p, "gopkg.in/") && func() bool {
        _, _, ok := module.SplitPathVersion(p); return ok
    }()
}

Try / catch

out, err := exec.Command("go", "mod", "init", path).CombinedOutput()
if err != nil && bytes.Contains(out, []byte("gopkg.in/ must always have a major version suffix")) {
    // use the suggestion embedded in the error message
    suggested := extractSuggestedPath(out) // your helper parses the 'go mod init ...' hint
    out, err = exec.Command("go", "mod", "init", suggested).CombinedOutput()
}
return err

Prevention

When it happens

Trigger: 'go mod init gopkg.in/user/project' (no .vN) while SplitPathVersion fails. The gopkg.in branch is taken and the user is told the canonical form via the suggestion.

Common situations: User unfamiliar with the gopkg.in convention; copying a GitHub-style path verbatim; migrating from dep/glide which did not enforce the suffix.

Related errors


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/8bcab2b260b2f6a5. Report an issue: GitHub.