{"record":{"id":"334bcfd4d5baa6cf","repo":"golang/go","slug":"ref-q-moved-from-s-to-s","errorCode":null,"errorMessage":"ref %q moved from %s to %s","messagePattern":"ref %q moved from (.+?) to (.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/cmd/go/internal/modfetch/codehost/git.go","lineNumber":227,"sourceCode":"\t// In that case we assume it does in the absence of any real way to check.\n\t// But if neither Hash nor TagSum is present, we have nothing to check,\n\t// which we take to mean we didn't record enough information to be sure.\n\tif old.Hash == \"\" && old.TagSum == \"\" && old.RepoSum == \"\" {\n\t\treturn fmt.Errorf(\"non-specific origin\")\n\t}\n\n\tr.loadRefs(ctx)\n\tif r.refsErr != nil {\n\t\treturn r.refsErr\n\t}\n\n\tif old.Ref != \"\" {\n\t\thash, ok := r.refs[old.Ref]\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"ref %q deleted\", old.Ref)\n\t\t}\n\t\tif hash != old.Hash {\n\t\t\treturn fmt.Errorf(\"ref %q moved from %s to %s\", old.Ref, old.Hash, hash)\n\t\t}\n\t}\n\tif old.TagSum != \"\" {\n\t\ttags, err := r.Tags(ctx, old.TagPrefix)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif tags.Origin.TagSum != old.TagSum {\n\t\t\treturn fmt.Errorf(\"tags changed\")\n\t\t}\n\t}\n\tif old.RepoSum != \"\" {\n\t\tif r.repoSum(r.refs) != old.RepoSum {\n\t\t\treturn fmt.Errorf(\"refs changed\")\n\t\t}\n\t}\n\treturn nil\n}","sourceCodeStart":209,"sourceCodeEnd":245,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/cmd/go/internal/modfetch/codehost/git.go#L209-L245","documentation":"Thrown by gitRepo.CheckReuse when the recorded ref still exists in the remote refs but now points to a different hash than old.Hash — the ref was updated (e.g. branch moved forward, tag was moved). This means the cached checkout may be stale relative to the ref it claims to track.","triggerScenarios":"CheckReuse on a module sourced from a mutable ref (branch or moved tag) where the upstream ref now resolves to a different commit. The cached repo was recorded against old.Hash but the remote now has a new hash for old.Ref.","commonSituations":"Depending on a branch (not a tag) that received new commits; a tag that was force-moved (unusual but possible);a pseudo-version whose underlying ref advanced; using 'latest' or a branch name in go get.","solutions":["Update the dependency to the new ref: 'go get module@<ref>'.","Pin to a specific immutable version tag or commit hash instead of a mutable branch.","If a moved tag is the issue, coordinate with the upstream maintainer.","Run 'go mod tidy' after updating to reconcile transitive deps."],"exampleFix":"// before: tracking a branch that moved\ngo get example.com/mod@main\n// after: pin to a specific tag\ngo get example.com/mod@v1.4.0","handlingStrategy":"fallback","validationCode":"null","typeGuard":"null","tryCatchPattern":"// On 'ref moved', re-stat against the new ref\nerr := repo.CheckReuse(ctx, old, subdir)\nif err != nil && strings.Contains(err.Error(), \"moved\") {\n    // accept that the ref advanced; re-resolve\n    info, ferr := repo.Stat(ctx, old.Ref)\n    if ferr == nil { return useInfo(info) }\n    return ferr\n}","preventionTips":["Avoid depending on mutable branch refs for reproducible builds.","Pin to semantic version tags or commit hashes in go.mod.","Run 'go mod tidy' regularly to catch ref drift early."],"tags":["git","cache-reuse","ref-moved","mutable-ref","upstream-change"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:31:55.035Z"}