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
- Validate the version with gover.ModIsValid before calling CachePath
- Resolve symbolic versions (like 'latest') to concrete semver before passing to cache APIs
- 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
- Always resolve symbolic versions via Stat() before calling cache APIs
- Validate versions with gover.ModIsValid before passing to modfetch internals
- Prefer high-level modload APIs over direct modfetch calls
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
- invalid version %q
- invalid version interval: %q
- non-canonical module version %q
- expecting a Go version like %q
- maximum supported Go version is %s
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/6cb2b4a847e428c3.
Report an issue: GitHub.