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 {
breakView on GitHub (pinned to b6b368adc5)
Solutions
- Reference the canonical tag directly in go.mod instead of the pseudo-version.
- Run `go get <module>@<tag>` so the tooling picks the tag.
- 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
- Prefer canonical tags over pseudo-versions whenever a tag exists.
- Use `go get <module>@<tag>` instead of `@<commit>` when the tag is known.
- Proxy operators: preserve tag metadata so clients do not synthesize redundant pseudo-versions.
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
- resolves to version %v (%s is not a tag)
- major version without preceding tag must be v0, not v1
- preceding tag (%s) not found
- not a descendent of preceding tag (%s)
- revision %s is not a descendent of preceding tag (%s)
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/e2358282194eacf4.
Report an issue: GitHub.