golang/go · error

major version without preceding tag must be v0, not v1

Error message

major version without preceding tag must be v0, not v1

What it means

validatePseudoVersion (coderepo.go:738-741) handles the no-preceding-tag case (empty base). For untagged repositories the first major version permitted is v0; a v1.x pseudo-version with no prior tag is rejected because v1.0.0 is conventionally the first stable release and should be expressed as a real tag, not a pseudo-version. Only when r.pseudoMajor overrides (gopkg.in-style) is a non-v0 first version allowed.

Source

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

	//
	// Moreover, if we allow a pseudo-version to use any arbitrary pre-release
	// tag, we end up with infinitely many possible names for each commit. Each
	// name consumes resources in the module cache and proxies, so we want to
	// restrict them to a finite set under control of the module author.
	//
	// We address both of these issues by requiring the tag upon which the
	// pseudo-version is based to refer to some ancestor of the revision. We
	// prefer the highest such tag when constructing a new pseudo-version, but do
	// not enforce that property when resolving existing pseudo-versions: we don't
	// know when the parent tags were added, and the highest-tagged parent may not
	// have existed when the pseudo-version was first resolved.
	base, err := module.PseudoVersionBase(strings.TrimSuffix(version, "+incompatible"))
	if err != nil {
		return err
	}
	if base == "" {
		if r.pseudoMajor == "" && semver.Major(version) == "v1" {
			return fmt.Errorf("major version without preceding tag must be v0, not v1")
		}
		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

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Use a v0.0.0-<timestamp>-<hash> pseudo-version for untagged repos instead of v1.x.
  2. If v1 is genuinely intended, publish a v1.0.0 tag in the source repo and depend on that.
  3. For gopkg.in-style modules, ensure the path carries the major suffix (.v1) so pseudoMajor is set.

Example fix

// before
require example.com/m v1.0.0-20240101120000-e3702bed3d42

// after (untagged repo must start at v0)
require example.com/m v0.0.0-20240101120000-e3702bed3d42
Defensive patterns

Strategy: validation

Validate before calling

// For untagged modules, never request v1.x pseudo-versions.
// bash:
//   git tag --list   # if empty, the module is untagged -> use v0.0.0-…
//
// Go: use the tooling to derive the correct major:
//   go get example.com/m@<commit>   // toolchain emits v0.0.0-… for untagged repos

Prevention

When it happens

Trigger: An untagged module's first pseudo-version is requested at v1.0.0-x format (no v0.x or v1.x tag exists). The repo has never published any tag, but the requested version claims major v1.

Common situations: A new module that has never been tagged but whose dependency is hand-written as v1.x; misinterpreting the pseudo-version format and starting at v1 instead of v0; legacy repos migrated without preserving tags.

Related errors


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