{"record":{"id":"ab17c2647455b8d0","repo":"golang/go","slug":"too-long","errorCode":null,"errorMessage":"too long","messagePattern":"too long","errorType":"exception","errorClass":"entryNotFoundError","httpStatus":null,"severity":"error","filePath":"src/cmd/go/internal/cache/cache.go","lineNumber":212,"sourceCode":"type Entry struct {\n\tOutputID OutputID\n\tSize     int64\n\tTime     time.Time // when added to cache\n}\n\n// get is Get but does not respect verify mode, so that Put can use it.\nfunc (c *DiskCache) get(id ActionID) (Entry, error) {\n\tmissing := func(reason error) (Entry, error) {\n\t\treturn Entry{}, &entryNotFoundError{Err: reason}\n\t}\n\tf, err := os.Open(c.fileName(id, \"a\"))\n\tif err != nil {\n\t\treturn missing(err)\n\t}\n\tdefer f.Close()\n\tentry := make([]byte, entrySize+1) // +1 to detect whether f is too long\n\tif n, err := io.ReadFull(f, entry); n > entrySize {\n\t\treturn missing(errors.New(\"too long\"))\n\t} else if err != io.ErrUnexpectedEOF {\n\t\tif err == io.EOF {\n\t\t\treturn missing(errors.New(\"file is empty\"))\n\t\t}\n\t\treturn missing(err)\n\t} else if n < entrySize {\n\t\treturn missing(errors.New(\"entry file incomplete\"))\n\t}\n\tif entry[0] != 'v' || entry[1] != '1' || entry[2] != ' ' || entry[3+hexSize] != ' ' || entry[3+hexSize+1+hexSize] != ' ' || entry[3+hexSize+1+hexSize+1+20] != ' ' || entry[entrySize-1] != '\\n' {\n\t\treturn missing(errors.New(\"invalid header\"))\n\t}\n\teid, entry := entry[3:3+hexSize], entry[3+hexSize:]\n\teout, entry := entry[1:1+hexSize], entry[1+hexSize:]\n\tesize, entry := entry[1:1+20], entry[1+20:]\n\tetime, entry := entry[1:1+20], entry[1+20:]\n\tvar buf [HashSize]byte\n\tif _, err := hex.Decode(buf[:], eid); err != nil {\n\t\treturn missing(fmt.Errorf(\"decoding ID: %v\", err))","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/cmd/go/internal/cache/cache.go#L194-L230","documentation":"The Go build cache stores each cache entry as a fixed-size metadata file (~175 bytes) with a version-prefixed header ('v1 ...'). DiskCache.get() allocates a buffer of entrySize+1 bytes and uses io.ReadFull to detect files longer than the expected format. If the read returns more than entrySize bytes, the entry file is larger than the known format, indicating corruption or version incompatibility.","triggerScenarios":"DiskCache.get(id) is invoked when looking up any cache entry (via Get, GetFile, GetBytes, GetMmap), and the 'a' (action) file on disk contains at least entrySize+1 bytes — io.ReadFull successfully fills the entire entrySize+1 buffer.","commonSituations":"A newer Go toolchain version changed the cache entry format; cache directory shared across machines or CI runners with different Go versions; disk corruption or filesystem errors extending the file; antivirus or backup software modifying cache files.","solutions":["Run go clean -cache to delete and rebuild the entire cache","Ensure all builds sharing a GOCACHE directory use the same Go version","Move GOCACHE off network/shared storage to a local path","Verify filesystem integrity with fsck or equivalent disk diagnostics"],"exampleFix":"// before: shared GOCACHE across Go versions causes format mismatch\n// $ GOCACHE=/shared/cache go build ./...\n\n// after: use separate cache per Go version\n// $ GOCACHE=/tmp/go-cache-$(go version | awk '{print $3}') go build ./...\n\n// or simply clean and rebuild\n// $ go clean -cache && go build ./...","handlingStrategy":"try-catch","validationCode":"// Before relying on cache entries, verify the cache directory health\nimport \"os\"\n\nfunc cacheHealthy(cacheDir string) bool {\n    info, err := os.Stat(cacheDir)\n    if err != nil || !info.IsDir() {\n        return false\n    }\n    // Try writing and reading a test file\n    testFile := filepath.Join(cacheDir, \".healthcheck\")\n    if err := os.WriteFile(testFile, []byte(\"ok\"), 0644); err != nil {\n        return false\n    }\n    os.Remove(testFile)\n    return true\n}","typeGuard":"// entryNotFoundError is the error type wrapping all cache read failures.\n// All errors 80-89 are wrapped in *entryNotFoundError via the missing() closure.\n// Since entryNotFoundError is unexported, use errors.As with the exported interface.\n\nimport \"errors\"\n\nfunc isCacheMiss(err error) bool {\n    // entryNotFoundError.Error() starts with \"cache entry not found\"\n    // It also implements Unwrap() so the inner error is accessible\n    var target interface{ Unwrap() error }\n    // In practice, Go toolchain internals use a local check:\n    // errors.Is(err, errMissing) or type assertion on *entryNotFoundError\n    return err != nil && strings.HasPrefix(err.Error(), \"cache entry not found\")\n}","tryCatchPattern":"// All cache read errors (80-89) are wrapped in *entryNotFoundError.\n// The canonical pattern (used internally by the Go toolchain) is:\n\nentry, err := cache.Get(actionID)\nif err != nil {\n    // Treat ANY cache error as a miss — rebuild the output\n    // The error message contains the specific reason (too long, file is empty, etc.)\n    // but for recovery purposes, all should be handled identically: rebuild.\n    output = rebuildOutput()\n} else {\n    useCachedEntry(entry)\n}","preventionTips":["Run go clean -cache after upgrading Go versions to avoid format mismatches","Never share a GOCACHE directory across machines or Go versions","Ensure the cache directory is on reliable local storage, not network mounts","Monitor disk space — a full disk causes truncated cache writes","Avoid hard-killing builds (kill -9) which can leave corrupt cache entries"],"tags":["go","build-cache","corruption","disk-cache"],"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T06:17:24.410Z"}