{"record":{"id":"f54daf4f8d1eb063","repo":"golang/go","slug":"incomplete-progresponse-outputid","errorCode":null,"errorMessage":"incomplete ProgResponse OutputID","messagePattern":"incomplete ProgResponse OutputID","errorType":"exception","errorClass":"entryNotFoundError","httpStatus":null,"severity":"error","filePath":"src/cmd/go/internal/cache/prog.go","lineNumber":293,"sourceCode":"\tif err != nil {\n\t\treturn Entry{}, err // TODO(bradfitz): or entryNotFoundError? Audit callers.\n\t}\n\tif res.Miss {\n\t\treturn Entry{}, &entryNotFoundError{}\n\t}\n\te := Entry{\n\t\tSize: res.Size,\n\t}\n\tif res.Time != nil {\n\t\te.Time = *res.Time\n\t} else {\n\t\te.Time = time.Now()\n\t}\n\tif res.DiskPath == \"\" {\n\t\treturn Entry{}, &entryNotFoundError{errors.New(\"GOCACHEPROG didn't populate DiskPath on get hit\")}\n\t}\n\tif copy(e.OutputID[:], res.OutputID) != len(res.OutputID) {\n\t\treturn Entry{}, &entryNotFoundError{errors.New(\"incomplete ProgResponse OutputID\")}\n\t}\n\tc.noteOutputFile(e.OutputID, res.DiskPath)\n\treturn e, nil\n}\n\nfunc (c *ProgCache) noteOutputFile(o OutputID, diskPath string) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\tc.outputFile[o] = diskPath\n}\n\nfunc (c *ProgCache) OutputFile(o OutputID) string {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\treturn c.outputFile[o]\n}\n\nfunc (c *ProgCache) Put(a ActionID, file io.ReadSeeker) (_ OutputID, size int64, _ error) {","sourceCodeStart":275,"sourceCodeEnd":311,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/cmd/go/internal/cache/prog.go#L275-L311","documentation":"The GOCACHEPROG program returned an OutputID byte slice whose length doesn't match the expected hash size (32 bytes for SHA-256). The code copies res.OutputID into the fixed-size OutputID array and checks whether all bytes were copied. If copy returns fewer bytes than len(res.OutputID), the response's OutputID is shorter than HashSize, meaning the program sent a truncated or wrong-length hash.","triggerScenarios":"ProgCache.Get() calls copy(e.OutputID[:], res.OutputID) and the return value (bytes copied into the [32]byte array) != len(res.OutputID). This means len(res.OutputID) > 32 and the array couldn't hold it, or the copy was incomplete.","commonSituations":"The GOCACHEPROG program returns a truncated hash (e.g., 16 bytes instead of 32); protocol version mismatch where the program uses a different hash algorithm or size; bug in the cache program's response construction where OutputID is nil or empty on a hit.","solutions":["Fix the GOCACHEPROG program to return a 32-byte SHA-256 OutputID in every hit response","Ensure the cache program uses SHA-256 (not MD5, SHA-1, or another algorithm) to match the Go toolchain","Update the cache program to match the Go version's cacheprog protocol specification","Temporarily unset GOCACHEPROG to use the default disk cache","Add logging in the cache program to verify the OutputID length before sending responses"],"exampleFix":"// before: GOCACHEPROG returns wrong-length OutputID\n// (in the cache program's Get handler)\n// return &Response{Miss: false, DiskPath: path, OutputID: md5hash}  // 16 bytes!\n\n// after: use SHA-256 to produce 32-byte OutputID\n// hash := sha256.Sum256(outputData)\n// return &Response{\n//     Miss:     false,\n//     DiskPath: path,\n//     OutputID: hash[:],  // exactly 32 bytes\n//     Size:     int64(len(outputData)),\n// }","handlingStrategy":"validation","validationCode":"// Verify the GOCACHEPROG program returns 32-byte SHA-256 OutputIDs.\n// This can be tested by performing a Put followed by a Get and checking\n// the OutputID length in the response.\n//\n// SHA-256 produces 32 bytes. The Go toolchain expects exactly this.\n// If the program uses a different hash, it must be fixed.","typeGuard":"func isIncompleteOutputID(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"incomplete ProgResponse OutputID\")\n}","tryCatchPattern":"// entry, err := progCache.Get(id)\n// if err != nil {\n//     if isIncompleteOutputID(err) {\n//         // The cache program returned a wrong-length OutputID.\n//         // Fall back to disk cache.\n//         os.Unsetenv(\"GOCACHEPROG\")\n//         entry, err = diskCache.Get(id)\n//     }\n//     if err != nil {\n//         output = rebuild()\n//     }\n// }","preventionTips":["Verify the cache program uses SHA-256 (32 bytes) for OutputID — not MD5 (16) or SHA-1 (20)","Test the program with a round-trip Put+Get before production use","Review the cacheprog protocol specification (cmd/internal/cacheprog) the program implements","Keep the program updated to match your Go version","Add a response validation step in the cache program before sending"],"tags":["go","build-cache","cacheprog","protocol-violation","hash"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}