golang/go · error

non-canonical module version %q

Error message

non-canonical module version %q

What it means

Thrown by `modfetch.CachePath` when the module version passes `ModIsValid` but `module.CanonicalVersion(version) != version`. Canonical versions have no redundant components (e.g., 'v1.2.0' not 'v1.2', no '+build' metadata that's identical to base). The cache requires canonical versions so every module has exactly one cache location.

Source

Thrown at src/cmd/go/internal/modfetch/cache.go:60

	if err != nil {
		return "", err
	}
	return filepath.Join(cfg.GOMODCACHE, "cache/download", enc, "/@v"), nil
}

func CachePath(ctx context.Context, m module.Version, suffix string) (string, error) {
	if gover.IsToolchain(m.Path) {
		return "", ErrToolchain
	}
	dir, err := cacheDir(ctx, m.Path)
	if err != nil {
		return "", err
	}
	if !gover.ModIsValid(m.Path, m.Version) {
		return "", fmt.Errorf("non-semver module version %q", m.Version)
	}
	if module.CanonicalVersion(m.Version) != m.Version {
		return "", fmt.Errorf("non-canonical module version %q", m.Version)
	}
	encVer, err := module.EscapeVersion(m.Version)
	if err != nil {
		return "", err
	}
	return filepath.Join(dir, encVer+"."+suffix), nil
}

// DownloadDir returns the directory to which m should have been downloaded.
// An error will be returned if the module path or version cannot be escaped.
// An error satisfying errors.Is(err, fs.ErrNotExist) will be returned
// along with the directory if the directory does not exist or if the directory
// is not completely populated.
func DownloadDir(ctx context.Context, m module.Version) (string, error) {
	if gover.IsToolchain(m.Path) {
		return "", ErrToolchain
	}
	if err := checkCacheDir(ctx); err != nil {

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Canonicalize the version before calling CachePath: `version = module.CanonicalVersion(version)`
  2. Use module.EscapeVersion which also expects canonical input — canonicalize upstream
  3. Validate with `module.CanonicalVersion(v) == v` before calling cache APIs

Example fix

// before
path, err := modfetch.CachePath(ctx, module.Version{Path: m, Version: "v1.2"}, "zip")
// after
canon := module.CanonicalVersion("v1.2")
path, err := modfetch.CachePath(ctx, module.Version{Path: m, Version: canon}, "zip")
Defensive patterns

Strategy: validation

Validate before calling

import "golang.org/x/mod/module"

func canonicalizeVersion(v string) string {
    return module.CanonicalVersion(v)
}

// Before calling CachePath:
v = module.CanonicalVersion(v)
if v != originalV {
    // version was non-canonical, now fixed
}

Prevention

When it happens

Trigger: Calling CachePath with a version like 'v1.2' (should be 'v1.2.0'), 'v1.0.0+meta' where the metadata is non-significant, or 'v1.00.0' (non-canonical zero-padding). CanonicalVersion normalizes these, and if the result differs, the error fires.

Common situations: Receiving non-canonical versions from external sources (proxies, manifests); user-supplied versions that aren't normalized; bugs in version string construction in calling code.

Related errors


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