{"record":{"id":"6e96e72ba3a5ce15","repo":"golang/go","slug":"short-write-writing-body-to-gocacheprog-for-action","errorCode":null,"errorMessage":"short write writing body to GOCACHEPROG for action %x, output %x: wrote %v; expected %v","messagePattern":"short write writing body to GOCACHEPROG for action %x, output %x: wrote (.+?); expected (.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cmd/go/internal/cache/prog.go","lineNumber":247,"sourceCode":"\t\treturn err\n\t}\n\tif err := c.bw.WriteByte('\\n'); err != nil {\n\t\treturn err\n\t}\n\tif req.Body != nil && req.BodySize > 0 {\n\t\tif err := c.bw.WriteByte('\"'); err != nil {\n\t\t\treturn err\n\t\t}\n\t\te := base64.NewEncoder(base64.StdEncoding, c.bw)\n\t\twrote, err := io.Copy(e, req.Body)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := e.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif wrote != req.BodySize {\n\t\t\treturn fmt.Errorf(\"short write writing body to GOCACHEPROG for action %x, output %x: wrote %v; expected %v\",\n\t\t\t\treq.ActionID, req.OutputID, wrote, req.BodySize)\n\t\t}\n\t\tif _, err := c.bw.WriteString(\"\\\"\\n\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := c.bw.Flush(); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (c *ProgCache) Get(a ActionID) (Entry, error) {\n\tif !c.can[cacheprog.CmdGet] {\n\t\t// They can't do a \"get\". Maybe they're a write-only cache.\n\t\t//\n\t\t// TODO(bradfitz,bcmills): figure out the proper error type here. Maybe\n\t\t// errors.ErrUnsupported? Is entryNotFoundError even appropriate? There","sourceCodeStart":229,"sourceCodeEnd":265,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/cmd/go/internal/cache/prog.go#L229-L265","documentation":"Returned by the GOCACHEPROG client when forwarding a cache entry body to the external cache subprocess: io.Copy from req.Body wrote fewer bytes than req.BodySize promised. The JSON-line protocol frames the body as a base64 string whose length must equal BodySize, so a short copy means the body stream ended early — either the body reader was mis-sized or the framing is inconsistent.","triggerScenarios":"GOCACHEPROG is set to a helper program; Put is called with req.Body whose actual length != req.BodySize; the Body reader returns EOF early; a buggy GOCACHEPROG closes its stdin mid-stream.","commonSituations":"Third-party or custom GOCACHEPROG with a size-accounting bug; pipe/SIGPIPE issues when the helper exits early; racing teardown of the helper process.","solutions":["Verify req.Body length matches req.BodySize at the call site before invoking Put.","Update or replace the GOCACHEPROG helper; check its logs for early exit.","Temporarily unset GOCACHEPROG to fall back to the built-in disk cache and confirm the helper is the cause.","Ensure the helper does not close stdin before consuming the full body."],"exampleFix":"// before: body and size disagree\nreq := &cache.PutReq{Body: someReader, BodySize: 1024}\n\n// after: derive size from the same source\nreq := &cache.PutReq{Body: bytes.NewReader(buf), BodySize: int64(len(buf))}","handlingStrategy":"validation","validationCode":"// ensure body size matches the reader before calling Put\nif _, ok := body.(io.ReadSeeker); ok {\n    n, _ := io.Copy(io.Discard, body.(io.ReadSeeker).Clone())\n    body.(io.ReadSeeker).Seek(0, io.SeekStart)\n    if n != req.BodySize { return fmt.Errorf(\"size mismatch\") }\n}","typeGuard":null,"tryCatchPattern":"if err := c.Put(ctx, req); err != nil && strings.Contains(err.Error(), \"short write\") {\n    // helper closed early; log and retry, or disable GOCACHEPROG\n    os.Unsetenv(\"GOCACHEPROG\")\n}","preventionTips":["Derive BodySize from the same bytes you feed into Body.","Prefer *bytes.Reader so length is known exactly.","Keep GOCACHEPROG helper logs visible during integration."],"tags":["cache","gocacheprog","io","subprocess"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}