{"record":{"id":"6fe35e894a91f3cd","repo":"golang/go","slug":"zip-for-s-has-unexpected-file-s","errorCode":null,"errorMessage":"zip for %s has unexpected file %s","messagePattern":"zip for (.+?) has unexpected file (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cmd/go/internal/modfetch/fetch.go","lineNumber":344,"sourceCode":"\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Double-check that the paths within the zip file are well-formed.\n\t//\n\t// TODO(bcmills): There is a similar check within the Unzip function. Can we eliminate one?\n\tfi, err := file.Stat()\n\tif err != nil {\n\t\treturn err\n\t}\n\tz, err := zip.NewReader(file, fi.Size())\n\tif err != nil {\n\t\treturn err\n\t}\n\tprefix := mod.Path + \"@\" + mod.Version + \"/\"\n\tfor _, zf := range z.File {\n\t\tif !strings.HasPrefix(zf.Name, prefix) {\n\t\t\treturn fmt.Errorf(\"zip for %s has unexpected file %s\", prefix[:len(prefix)-1], zf.Name)\n\t\t}\n\t}\n\n\tif err := file.Close(); err != nil {\n\t\treturn err\n\t}\n\n\t// Hash the zip file and check the sum before renaming to the final location.\n\tif err := hashZip(f, mod, file.Name(), ziphashfile); err != nil {\n\t\treturn err\n\t}\n\tif err := os.Rename(file.Name(), zipfile); err != nil {\n\t\treturn err\n\t}\n\n\t// TODO(bcmills): Should we make the .zip and .ziphash files read-only to discourage tampering?\n\n\treturn nil","sourceCodeStart":326,"sourceCodeEnd":362,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/cmd/go/internal/modfetch/fetch.go#L326-L362","documentation":"When the go command verifies a cached module zip it requires every entry name to begin with `{module.Path}@{module.Version}/`. Any entry missing that prefix is reported as 'unexpected file' — a guard against cache poisoning and malformed archives that has the module-hash check downstream.","triggerScenarios":"fetch.go opens the cached zip, iterates z.File, and finds an entry whose Name does not have the module@version/ prefix. A hand-edited cache, a partial write, or a proxy returning the wrong module triggers it.","commonSituations":"Stale or partially-written cache after an interrupted download; manual edits to GOMODCACHE; mismatched GOFLAGS/-insecure; disk corruption.","solutions":["Run go clean -modcache to wipe and rebuild the cache from scratch.","Verify GOMODACHE is not shared with non-Go tooling that writes into it.","Re-run go mod download for the specific module to repopulate just that entry."],"exampleFix":"// before: cache contains stray top-level files\n// terminal\n//   go clean -modcache\n//   go mod download","handlingStrategy":"validation","validationCode":"// Confirm cache integrity before relying on it.\n//   go mod verify\n// In code, validate the prefix invariant on any cached zip you open:\nfunc cacheZipPrefixOK(mod module.Version, names []string) error {\n    prefix := mod.Path + \"@\" + mod.Version + \"/\"\n    for _, n := range names {\n        if !strings.HasPrefix(n, prefix) {\n            return fmt.Errorf(\"unexpected entry %q\", n)\n        }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Run `go mod verify` in CI to catch cache corruption early.","Never let non-Go tooling write into GOMODCACHE.","Use go clean -modcache after interrupted downloads."],"tags":["zip","cache","verification","modcache"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}