golang/go · error

short write writing body to GOCACHEPROG for action

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.

Solutions

  1. Verify req.Body length matches req.BodySize at the call site before invoking Put.
  2. Update or replace the GOCACHEPROG helper; check its logs for early exit.
  3. Temporarily unset GOCACHEPROG to fall back to the built-in disk cache and confirm the helper is the cause.
  4. 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

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


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/6e96e72ba3a5ce15. Report an issue: GitHub.

Appendix: 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? There

View on GitHub (pinned to b6b368adc5)