{"record":{"id":"50a75531d03f6236","repo":"golang/go","slug":"hash-changed","errorCode":null,"errorMessage":"hash changed","messagePattern":"hash changed","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/cmd/go/internal/modfetch/codehost/vcs.go","lineNumber":400,"sourceCode":"\t\treturn fmt.Errorf(\"missing origin\")\n\t}\n\tif old.VCS != r.cmd.vcs || old.URL != r.remote {\n\t\treturn fmt.Errorf(\"origin moved from %v %q to %v %q\", old.VCS, old.URL, r.cmd.vcs, r.remote)\n\t}\n\tif old.Subdir != subdir {\n\t\treturn fmt.Errorf(\"origin moved from %v %q %q to %v %q %q\", old.VCS, old.URL, old.Subdir, r.cmd.vcs, r.remote, subdir)\n\t}\n\n\tif old.Ref == \"\" && old.RepoSum == \"\" && old.Hash != \"\" {\n\t\t// Hash has to remain in repo.\n\t\thash, err := r.lookupRef(ctx, old.Hash)\n\t\tif err == nil && hash == old.Hash {\n\t\t\treturn nil\n\t\t}\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"looking up hash: %v\", err)\n\t\t}\n\t\treturn fmt.Errorf(\"hash changed\") // weird but maybe they made a tag\n\t}\n\n\tif old.Ref != \"\" && old.RepoSum == \"\" {\n\t\thash, err := r.lookupRef(ctx, old.Ref)\n\t\tif err == nil && hash != \"\" && hash == old.Hash {\n\t\t\treturn nil\n\t\t}\n\t}\n\n\tr.repoSumOnce.Do(func() { r.loadRepoSum(ctx) })\n\tif r.repoSum != \"\" {\n\t\tif old.RepoSum == \"\" {\n\t\t\treturn fmt.Errorf(\"non-specific origin\")\n\t\t}\n\t\tif old.RepoSum != r.repoSum {\n\t\t\treturn fmt.Errorf(\"repo changed\")\n\t\t}\n\t\treturn nil","sourceCodeStart":382,"sourceCodeEnd":418,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/cmd/go/internal/modfetch/codehost/vcs.go#L382-L418","documentation":"Hash verification branch: lookupRef succeeded but returned a different hash than old.Hash. The inline comment says 'weird but maybe they made a tag' — this means the ref now resolves to a different commit, indicating the tag/branch was reassigned or the repo was rewritten.","triggerScenarios":"old.Hash recorded a specific commit, lookupRef(old.Hash) returns a non-matching hash — upstream force-pushed, retagged, or rewrote history at that name.","commonSituations":"Upstream retagged a release to a different commit (rare but happens after yanks); a branch used as a ref was reset; repository was rewritten with git filter-branch.","solutions":["Pin to an explicit semver tag in go.mod instead of a branch or commit that can move.","Run `go clean -modcache` and re-resolve — the new hash becomes the baseline.","If you control the upstream, avoid retagging; if a yank is needed, use module retraction instead.","Audit go.sum for the affected module and update checksums after confirming the new commit is legitimate."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"func hashStable(oldHash, resolvedHash string) bool {\n    return oldHash == resolvedHash\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never retag released versions — use module retraction instead.","Avoid force-pushing shared release branches.","Pin go.mod entries to canonical semver tags."],"tags":["vcs","checkreuse","hash","force-push","retag"],"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T06:17:24.410Z"}