golang/go · error

tag (%s) found on revision %s is already canonical, so shoul

Error message

tag (%s) found on revision %s is already canonical, so should not be replaced with a pseudo-version derived from that tag

What it means

validatePseudoVersion (coderepo.go:744-762) walks info.Tags looking for one equal to the pseudo-version's base. If a tag with that exact canonical name is found on the very revision the pseudo-version points to, the pseudo-version is redundant and confusing: the canonical tag already exists and already gives a total ordering. The resolver demands that you use the tag directly.

Source

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

		return nil
	} else {
		for _, tag := range info.Tags {
			versionOnly := strings.TrimPrefix(tag, tagPrefix)
			if versionOnly == base {
				// The base version is canonical, so if the version from the tag is
				// literally equal (not just equivalent), then the tag is canonical too.
				//
				// We allow pseudo-versions to be derived from non-canonical tags on the
				// same commit, so that tags like "v1.1.0+some-metadata" resolve as
				// close as possible to the canonical version ("v1.1.0") while still
				// enforcing a total ordering ("v1.1.1-0.[…]" with a unique suffix).
				//
				// However, canonical tags already have a total ordering, so there is no
				// reason not to use the canonical tag directly, and we know that the
				// canonical tag must already exist because the pseudo-version is
				// derived from it. In that case, referring to the revision by a
				// pseudo-version derived from its own canonical tag is just confusing.
				return fmt.Errorf("tag (%s) found on revision %s is already canonical, so should not be replaced with a pseudo-version derived from that tag", tag, rev)
			}
		}
	}

	tags, err := r.code.Tags(ctx, tagPrefix+base)
	if err != nil {
		return err
	}

	var lastTag string // Prefer to log some real tag rather than a canonically-equivalent base.
	ancestorFound := false
	for _, tag := range tags.List {
		versionOnly := strings.TrimPrefix(tag.Name, tagPrefix)
		if semver.Compare(versionOnly, base) == 0 {
			lastTag = tag.Name
			ancestorFound, err = r.code.DescendsFrom(ctx, info.Name, tag.Name)
			if ancestorFound {
				break

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Reference the canonical tag directly in go.mod instead of the pseudo-version.
  2. Run `go get <module>@<tag>` so the tooling picks the tag.
  3. If you are a proxy operator, ensure tags are preserved so clients do not synthesize pseudo-versions.

Example fix

// before (commit also tagged v1.2.3)
require example.com/m v1.2.4-0.20240101120000-e3702bed3d42

// after (use the canonical tag)
require example.com/m v1.2.3
Defensive patterns

Strategy: validation

Validate before calling

// If the commit is already tagged, prefer the tag.
// bash:
//   git tag --points-at <commit>
//   # if it lists a canonical semver tag, depend on that tag instead
//
// In Go: resolve by tag first
//   go get example.com/m@v1.2.3   // not the pseudo-version for the same commit

Prevention

When it happens

Trigger: A commit carries tag v1.2.3 and someone references that same commit via a pseudo-version like v1.2.4-0.20240101-e3702bed3d42 (or v1.2.3-0.…). Since v1.2.3 is canonical and on that commit, the pseudo-version is rejected.

Common situations: Tooling that always derives pseudo-versions even when a real tag exists; copy-pasting a commit hash instead of the tag that points to it; mirror/proxy that loses tag info and falls back to pseudo-versions.

Related errors


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