{"record":{"id":"303d4c477ca49b52","repo":"golang/go","slug":"gocacheprog-didn-t-return-diskpath-in-put-response","errorCode":null,"errorMessage":"GOCACHEPROG didn't return DiskPath in put response","messagePattern":"GOCACHEPROG didn't return DiskPath in put response","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cmd/go/internal/cache/prog.go","lineNumber":344,"sourceCode":"\t}\n\n\tif !c.can[cacheprog.CmdPut] {\n\t\t// Child is a read-only cache. Do nothing.\n\t\treturn out, size, nil\n\t}\n\n\tres, err := c.send(c.ctx, &cacheprog.Request{\n\t\tCommand:  cacheprog.CmdPut,\n\t\tActionID: a[:],\n\t\tOutputID: out[:],\n\t\tBody:     file,\n\t\tBodySize: size,\n\t})\n\tif err != nil {\n\t\treturn OutputID{}, 0, err\n\t}\n\tif res.DiskPath == \"\" {\n\t\treturn OutputID{}, 0, errors.New(\"GOCACHEPROG didn't return DiskPath in put response\")\n\t}\n\tc.noteOutputFile(out, res.DiskPath)\n\treturn out, size, err\n}\n\nfunc (c *ProgCache) Close() error {\n\tc.closing.Store(true)\n\tvar err error\n\n\t// First write a \"close\" message to the child so it can exit nicely\n\t// and clean up if it wants. Only after that exchange do we cancel\n\t// the context that kills the process.\n\tif c.can[cacheprog.CmdClose] {\n\t\t_, err = c.send(c.ctx, &cacheprog.Request{Command: cacheprog.CmdClose})\n\t\tif errors.Is(err, errCacheprogClosed) {\n\t\t\t// Allow the child to quit without responding to close.\n\t\t\terr = nil\n\t\t}","sourceCodeStart":326,"sourceCodeEnd":362,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/cmd/go/internal/cache/prog.go#L326-L362","documentation":"After sending a Put command to the GOCACHEPROG program, the response did not include a DiskPath field. A Put response must tell the Go toolchain where the output was stored on disk so it can be referenced later. An empty DiskPath means the program didn't properly persist the output or didn't report its location — violating the protocol.","triggerScenarios":"ProgCache.Put() sends a cacheprog.Request with Command=CmdPut, ActionID, OutputID, Body (file path), and BodySize. The response arrives with res.DiskPath == ''.","commonSituations":"Bug in the GOCACHEPROG program's Put handler — it stores data but doesn't return the storage path; storage backend failure that the program swallows instead of reporting as res.Err; protocol version mismatch where the program's response format is outdated; the program uses an abstract storage model that doesn't map to disk paths.","solutions":["Fix or update the GOCACHEPROG program's Put handler to always return DiskPath after successful storage","Check the cache program's logs for storage errors or swallowed exceptions","Temporarily unset GOCACHEPROG to use the default disk cache while debugging","Verify the cache program implements the correct cacheprog protocol version for your Go release","Add error handling in the program to set res.Err instead of silently returning an incomplete response"],"exampleFix":"// before: GOCACHEPROG Put handler omits DiskPath\n// func handlePut(req *Request) *Response {\n//     store.Save(req.ActionID, req.Body)\n//     return &Response{}  // missing DiskPath!\n// }\n\n// after: always return DiskPath on Put\n// func handlePut(req *Request) *Response {\n//     path, err := store.Save(req.ActionID, req.Body)\n//     if err != nil {\n//         return &Response{Err: err.Error()}\n//     }\n//     return &Response{DiskPath: path}\n// }","handlingStrategy":"validation","validationCode":"// Verify the GOCACHEPROG program correctly handles Put by performing\n// a test Put and checking that the response includes DiskPath.\n// This requires speaking the cacheprog JSON protocol.\n//\n// At minimum, review the program's documentation to confirm Put responses\n// include DiskPath for your Go version's protocol.","typeGuard":"func isMissingPutDiskPath(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"didn't return DiskPath in put\")\n}","tryCatchPattern":"// outputID, size, err := progCache.Put(actionID, outputID, size, file)\n// if err != nil {\n//     if isMissingPutDiskPath(err) {\n//         // The cache program has a Put protocol bug.\n//         // Fall back to disk cache.\n//         os.Unsetenv(\"GOCACHEPROG\")\n//         outputID, size, err = diskCache.Put(actionID, outputID, size, file)\n//     }\n// }","preventionTips":["Test the cache program's Put handler with known inputs before production use","Review the program's Put implementation to verify it returns DiskPath","Ensure the program handles storage errors by setting res.Err rather than returning incomplete responses","Keep a fallback build configuration without GOCACHEPROG","File a bug with the program maintainer if Put responses lack DiskPath"],"tags":["go","build-cache","cacheprog","protocol-violation","ipc"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:31:55.035Z"}