golang/go · error · entryNotFoundError

incomplete ProgResponse OutputID

Error message

incomplete ProgResponse OutputID

What it means

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.

Source

Thrown at src/cmd/go/internal/cache/prog.go:293

	if err != nil {
		return Entry{}, err // TODO(bradfitz): or entryNotFoundError? Audit callers.
	}
	if res.Miss {
		return Entry{}, &entryNotFoundError{}
	}
	e := Entry{
		Size: res.Size,
	}
	if res.Time != nil {
		e.Time = *res.Time
	} else {
		e.Time = time.Now()
	}
	if res.DiskPath == "" {
		return Entry{}, &entryNotFoundError{errors.New("GOCACHEPROG didn't populate DiskPath on get hit")}
	}
	if copy(e.OutputID[:], res.OutputID) != len(res.OutputID) {
		return Entry{}, &entryNotFoundError{errors.New("incomplete ProgResponse OutputID")}
	}
	c.noteOutputFile(e.OutputID, res.DiskPath)
	return e, nil
}

func (c *ProgCache) noteOutputFile(o OutputID, diskPath string) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.outputFile[o] = diskPath
}

func (c *ProgCache) OutputFile(o OutputID) string {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.outputFile[o]
}

func (c *ProgCache) Put(a ActionID, file io.ReadSeeker) (_ OutputID, size int64, _ error) {

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Fix the GOCACHEPROG program to return a 32-byte SHA-256 OutputID in every hit response
  2. Ensure the cache program uses SHA-256 (not MD5, SHA-1, or another algorithm) to match the Go toolchain
  3. Update the cache program to match the Go version's cacheprog protocol specification
  4. Temporarily unset GOCACHEPROG to use the default disk cache
  5. Add logging in the cache program to verify the OutputID length before sending responses

Example fix

// before: GOCACHEPROG returns wrong-length OutputID
// (in the cache program's Get handler)
// return &Response{Miss: false, DiskPath: path, OutputID: md5hash}  // 16 bytes!

// after: use SHA-256 to produce 32-byte OutputID
// hash := sha256.Sum256(outputData)
// return &Response{
//     Miss:     false,
//     DiskPath: path,
//     OutputID: hash[:],  // exactly 32 bytes
//     Size:     int64(len(outputData)),
// }
Defensive patterns

Strategy: validation

Validate before calling

// Verify the GOCACHEPROG program returns 32-byte SHA-256 OutputIDs.
// This can be tested by performing a Put followed by a Get and checking
// the OutputID length in the response.
//
// SHA-256 produces 32 bytes. The Go toolchain expects exactly this.
// If the program uses a different hash, it must be fixed.

Type guard

func isIncompleteOutputID(err error) bool {
    return err != nil && strings.Contains(err.Error(), "incomplete ProgResponse OutputID")
}

Try / catch

// entry, err := progCache.Get(id)
// if err != nil {
//     if isIncompleteOutputID(err) {
//         // The cache program returned a wrong-length OutputID.
//         // Fall back to disk cache.
//         os.Unsetenv("GOCACHEPROG")
//         entry, err = diskCache.Get(id)
//     }
//     if err != nil {
//         output = rebuild()
//     }
// }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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