{"record":{"id":"eba1e27dfbc6161c","repo":"golang/go","slug":"ambiguous-semantic-version-q-in-range-q","errorCode":null,"errorMessage":"ambiguous semantic version %q in range %q","messagePattern":"ambiguous semantic version %q in range %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cmd/go/internal/modload/query.go","lineNumber":478,"sourceCode":"\t\tif current == \"\" || current == \"none\" {\n\t\t\treturn nil, &NoPatchBaseError{path}\n\t\t}\n\t\tif current == \"\" {\n\t\t\tqm.mayUseLatest = true\n\t\t} else {\n\t\t\tqm.mayUseLatest = module.IsPseudoVersion(current)\n\t\t\tqm.prefix = gover.ModMajorMinor(qm.path, current) + \".\"\n\t\t\tqm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, current) >= 0 }\n\t\t}\n\n\tcase strings.HasPrefix(query, \"<=\"):\n\t\tv := query[len(\"<=\"):]\n\t\tif !gover.ModIsValid(path, v) {\n\t\t\treturn badVersion(v)\n\t\t}\n\t\tif gover.ModIsPrefix(path, v) {\n\t\t\t// Refuse to say whether <=v1.2 allows v1.2.3 (remember, @v1.2 might mean v1.2.3).\n\t\t\treturn nil, fmt.Errorf(\"ambiguous semantic version %q in range %q\", v, query)\n\t\t}\n\t\tqm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, v) <= 0 }\n\t\tif !matchesMajor(v) {\n\t\t\tqm.preferIncompatible = true\n\t\t}\n\n\tcase strings.HasPrefix(query, \"<\"):\n\t\tv := query[len(\"<\"):]\n\t\tif !gover.ModIsValid(path, v) {\n\t\t\treturn badVersion(v)\n\t\t}\n\t\tqm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, v) < 0 }\n\t\tif !matchesMajor(v) {\n\t\t\tqm.preferIncompatible = true\n\t\t}\n\n\tcase strings.HasPrefix(query, \">=\"):\n\t\tv := query[len(\">=\"):]","sourceCodeStart":460,"sourceCodeEnd":496,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/cmd/go/internal/modload/query.go#L460-L496","documentation":"Returned by newQueryMatcher for a '<=' query whose bound is a version prefix (e.g. v1.2) rather than a full canonical version. Because '@v1.2' may resolve to v1.2.3, the toolchain cannot decide unambiguously whether '<=v1.2' should include v1.2.3, so it rejects the query rather than guess.","triggerScenarios":"Running 'go get example.com/pkg@<=v1.2' or '@<=v2' where the bound omits the patch (and minor) component.","commonSituations":"Scripts building range queries from truncated version strings; users assuming v1.2 means exactly v1.2.0; CI matrices querying allowed version ceilings with shorthand versions.","solutions":["Write the bound as a full semver including patch: '<=v1.2.0' (which is unambiguous and excludes v1.2.3).","Or use '<v1.3.0' if the intent is 'everything in the 1.2 line'.","Validate with gover.ModIsPrefix — if true for your bound, expand it to canonical form before querying."],"exampleFix":"// before\n$ go get example.com/pkg@<=v1.2\n// error: ambiguous semantic version \"v1.2\" in range \"<=v1.2\"\n\n// after\n$ go get example.com/pkg@<=v1.2.0   // explicit, excludes v1.2.3","handlingStrategy":"validation","validationCode":"// Expand prefix bounds to canonical form for '<=' queries:\n//   if gover.ModIsPrefix(path, v) { v = strings.TrimSuffix(v, \".\") + \".0\"; /* but prefer full version */ }\n// Better: require full versions: if gover.ModIsPrefix(path, v) { return ErrAmbiguous }","typeGuard":"func isUnambiguousLeBound(path, v string) bool {\n    return gover.ModIsValid(path, v) && !gover.ModIsPrefix(path, v)\n}","tryCatchPattern":null,"preventionTips":["Write '<=v1.2.0' (full semver) — never '<=v1.2'.","Reject prefix-style bounds in tooling that builds queries.","Document the v1.2-vs-v1.2.3 ambiguity for contributors writing version ranges."],"tags":["go-modules","query","semver","ambiguity"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}