golang/go · error

non-semver module version %q

Error message

non-semver module version %q

What it means

Thrown by `modfetch.CachePath` when `gover.ModIsValid(path, version)` returns false for the module path/version combination. ModIsValid checks that the version is syntactically valid semver (or a recognized non-semver form for certain paths). A non-semver version cannot be encoded into the cache directory structure. This is distinct from canonicality — it's about basic structural validity.

Source

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

		return "", err
	}
	enc, err := module.EscapePath(path)
	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) {

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Validate the version with gover.ModIsValid before calling CachePath
  2. Resolve symbolic versions (like 'latest') to concrete semver before passing to cache APIs
  3. Use the higher-level modload/modfetch APIs that handle version resolution automatically

Example fix

// before
path, err := modfetch.CachePath(ctx, module.Version{Path: m, Version: "latest"}, "zip")
// after
info, err := modfetch.Lookup(ctx, m).Stat(ctx, "latest")
path, err := modfetch.CachePath(ctx, module.Version{Path: m, Version: info.Version}, "zip")
Defensive patterns

Strategy: validation

Validate before calling

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

func safeCachePath(ctx context.Context, m module.Version, suffix string) (string, error) {
    if !gover.ModIsValid(m.Path, m.Version) {
        return "", fmt.Errorf("invalid version %q for path %q", m.Version, m.Path)
    }
    return modfetch.CachePath(ctx, m, suffix)
}

Prevention

When it happens

Trigger: Calling `modfetch.CachePath(ctx, module.Version{Path: path, Version: version}, suffix)` with a version like 'latest', 'HEAD', a raw commit hash not in recognized form, or a malformed semver string. This is an internal API called during module download/lookup when the version isn't a valid module version.

Common situations: Internal go command bugs; calling modfetch APIs directly with unvalidated versions; module resolution producing an invalid version string; race conditions during version resolution.

Related errors


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