golang/go · error
short write writing body to GOCACHEPROG for action %x, outpu
Error message
short write writing body to GOCACHEPROG for action %x, output %x: wrote %v; expected %v
What it means
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.
Source
Thrown at src/cmd/go/internal/cache/prog.go:247
return err
}
if err := c.bw.WriteByte('\n'); err != nil {
return err
}
if req.Body != nil && req.BodySize > 0 {
if err := c.bw.WriteByte('"'); err != nil {
return err
}
e := base64.NewEncoder(base64.StdEncoding, c.bw)
wrote, err := io.Copy(e, req.Body)
if err != nil {
return err
}
if err := e.Close(); err != nil {
return err
}
if wrote != req.BodySize {
return fmt.Errorf("short write writing body to GOCACHEPROG for action %x, output %x: wrote %v; expected %v",
req.ActionID, req.OutputID, wrote, req.BodySize)
}
if _, err := c.bw.WriteString("\"\n"); err != nil {
return err
}
}
if err := c.bw.Flush(); err != nil {
return err
}
return nil
}
func (c *ProgCache) Get(a ActionID) (Entry, error) {
if !c.can[cacheprog.CmdGet] {
// They can't do a "get". Maybe they're a write-only cache.
//
// TODO(bradfitz,bcmills): figure out the proper error type here. Maybe
// errors.ErrUnsupported? Is entryNotFoundError even appropriate? ThereView on GitHub (pinned to b6b368adc5)
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.
Example fix
// before: body and size disagree
req := &cache.PutReq{Body: someReader, BodySize: 1024}
// after: derive size from the same source
req := &cache.PutReq{Body: bytes.NewReader(buf), BodySize: int64(len(buf))} Defensive patterns
Strategy: validation
Validate before calling
// ensure body size matches the reader before calling Put
if _, ok := body.(io.ReadSeeker); ok {
n, _ := io.Copy(io.Discard, body.(io.ReadSeeker).Clone())
body.(io.ReadSeeker).Seek(0, io.SeekStart)
if n != req.BodySize { return fmt.Errorf("size mismatch") }
} Try / catch
if err := c.Put(ctx, req); err != nil && strings.Contains(err.Error(), "short write") {
// helper closed early; log and retry, or disable GOCACHEPROG
os.Unsetenv("GOCACHEPROG")
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: Third-party or custom GOCACHEPROG with a size-accounting bug; pipe/SIGPIPE issues when the helper exits early; racing teardown of the helper process.
Related errors
- error reading module index: %v
- bufio: reader returned negative count from Read
- bufio: writer returned negative count from Write
- bufio.Scanner: token too long
- gocacheverify=1
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/6e96e72ba3a5ce15.
Report an issue: GitHub.