golang/go · error

%s/%s and ...%s/go.mod both have ...%s module paths at revis

Error message

%s/%s and ...%s/go.mod both have ...%s module paths at revision %s

What it means

findDir (coderepo.go:868-870). Both the root go.mod (file1) and the major-subdir go.mod (file2) were read successfully and BOTH declare module paths whose major version matches pathMajor. The module system requires exactly one of them for a given major version; having both is ambiguous and rejected.

Source

Thrown at src/cmd/go/internal/modfetch/coderepo.go:869

	if r.pathMajor != "" && r.codeRoot != r.modPath && !strings.HasPrefix(r.pathMajor, ".") {
		// Suppose pathMajor is "/v2".
		// Either go.mod should claim v2 and v2/go.mod should not exist,
		// or v2/go.mod should exist and claim v2. Not both.
		// Note that we don't check the full path, just the major suffix,
		// because of replacement modules. This might be a fork of
		// the real module, found at a different path, usable only in
		// a replace directive.
		dir2 := path.Join(r.codeDir, r.pathMajor[1:])
		file2 = path.Join(dir2, "go.mod")
		gomod2, err2 := r.code.ReadFile(ctx, rev, file2, codehost.MaxGoMod)
		if err2 != nil && !os.IsNotExist(err2) {
			return "", "", nil, fmt.Errorf("reading %s/%s at revision %s: %v", r.codeRoot, file2, rev, err2)
		}
		mpath2 := modfile.ModulePath(gomod2)
		found2 := err2 == nil && isMajor(mpath2, r.pathMajor)

		if found1 && found2 {
			return "", "", nil, fmt.Errorf("%s/%s and ...%s/go.mod both have ...%s module paths at revision %s", r.pathPrefix, file1, r.pathMajor, r.pathMajor, rev)
		}
		if found2 {
			return rev, dir2, gomod2, nil
		}
		if err2 == nil {
			if mpath2 == "" {
				return "", "", nil, fmt.Errorf("%s/%s is missing module path at revision %s", r.codeRoot, file2, rev)
			}
			return "", "", nil, fmt.Errorf("%s/%s has non-...%s module path %q at revision %s", r.codeRoot, file2, r.pathMajor, mpath2, rev)
		}
	}

	// Not v2/go.mod, so it's either go.mod or nothing. Which is it?
	if found1 {
		// Explicit go.mod with matching major version ok.
		return rev, r.codeDir, gomod1, nil
	}
	if err1 == nil {

View on GitHub (pinned to b6b368adc5)

Solutions

  1. In the source repo, remove one of the conflicting go.mod files: keep either the root or the major-subdir layout, not both.
  2. If keeping /v2/go.mod, restore root go.mod to a v0/v1 module path.
  3. Tag a new revision after the cleanup and update the dependency.

Example fix

// before: repo layout
//   go.mod      -> module example.com/m/v2
//   v2/go.mod   -> module example.com/m/v2

// after: choose one layout
//   go.mod      -> module example.com/m      (root, v1)
//   v2/go.mod   -> module example.com/m/v2   (major subdir)
Defensive patterns

Strategy: validation

Validate before calling

// In the source repo, ensure exactly one go.mod declares the major version.
// bash:
//   grep -RE '^module .+/v[0-9]+$' go.mod v*/go.mod
//   # exactly one match expected for a given major version
// CI lint example:
//   test "$(grep -lE 'module .+/v2$' go.mod v2/go.mod 2>/dev/null | wc -l)" -le 1

Prevention

When it happens

Trigger: A repo at revision <rev> has both `<codeDir>/go.mod` declaring `module example.com/m/v2` AND `<codeDir>/v2/go.mod` declaring `module example.com/m/v2`. Both pass isMajor against pathMajor='/v2'.

Common situations: Migrating a module to a /v2 subdirectory without removing the old root go.mod's v2 declaration; botched major-version bump; tooling that auto-creates /v2/go.mod alongside an existing root file.

Related errors


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