golang/go · error · entryNotFoundError

too long

Error message

too long

What it means

The Go build cache stores each cache entry as a fixed-size metadata file (~175 bytes) with a version-prefixed header ('v1 ...'). DiskCache.get() allocates a buffer of entrySize+1 bytes and uses io.ReadFull to detect files longer than the expected format. If the read returns more than entrySize bytes, the entry file is larger than the known format, indicating corruption or version incompatibility.

Source

Thrown at src/cmd/go/internal/cache/cache.go:212

type Entry struct {
	OutputID OutputID
	Size     int64
	Time     time.Time // when added to cache
}

// get is Get but does not respect verify mode, so that Put can use it.
func (c *DiskCache) get(id ActionID) (Entry, error) {
	missing := func(reason error) (Entry, error) {
		return Entry{}, &entryNotFoundError{Err: reason}
	}
	f, err := os.Open(c.fileName(id, "a"))
	if err != nil {
		return missing(err)
	}
	defer f.Close()
	entry := make([]byte, entrySize+1) // +1 to detect whether f is too long
	if n, err := io.ReadFull(f, entry); n > entrySize {
		return missing(errors.New("too long"))
	} else if err != io.ErrUnexpectedEOF {
		if err == io.EOF {
			return missing(errors.New("file is empty"))
		}
		return missing(err)
	} else if n < entrySize {
		return missing(errors.New("entry file incomplete"))
	}
	if entry[0] != 'v' || entry[1] != '1' || entry[2] != ' ' || entry[3+hexSize] != ' ' || entry[3+hexSize+1+hexSize] != ' ' || entry[3+hexSize+1+hexSize+1+20] != ' ' || entry[entrySize-1] != '\n' {
		return missing(errors.New("invalid header"))
	}
	eid, entry := entry[3:3+hexSize], entry[3+hexSize:]
	eout, entry := entry[1:1+hexSize], entry[1+hexSize:]
	esize, entry := entry[1:1+20], entry[1+20:]
	etime, entry := entry[1:1+20], entry[1+20:]
	var buf [HashSize]byte
	if _, err := hex.Decode(buf[:], eid); err != nil {
		return missing(fmt.Errorf("decoding ID: %v", err))

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Run go clean -cache to delete and rebuild the entire cache
  2. Ensure all builds sharing a GOCACHE directory use the same Go version
  3. Move GOCACHE off network/shared storage to a local path
  4. Verify filesystem integrity with fsck or equivalent disk diagnostics

Example fix

// before: shared GOCACHE across Go versions causes format mismatch
// $ GOCACHE=/shared/cache go build ./...

// after: use separate cache per Go version
// $ GOCACHE=/tmp/go-cache-$(go version | awk '{print $3}') go build ./...

// or simply clean and rebuild
// $ go clean -cache && go build ./...
Defensive patterns

Strategy: try-catch

Validate before calling

// Before relying on cache entries, verify the cache directory health
import "os"

func cacheHealthy(cacheDir string) bool {
    info, err := os.Stat(cacheDir)
    if err != nil || !info.IsDir() {
        return false
    }
    // Try writing and reading a test file
    testFile := filepath.Join(cacheDir, ".healthcheck")
    if err := os.WriteFile(testFile, []byte("ok"), 0644); err != nil {
        return false
    }
    os.Remove(testFile)
    return true
}

Type guard

// entryNotFoundError is the error type wrapping all cache read failures.
// All errors 80-89 are wrapped in *entryNotFoundError via the missing() closure.
// Since entryNotFoundError is unexported, use errors.As with the exported interface.

import "errors"

func isCacheMiss(err error) bool {
    // entryNotFoundError.Error() starts with "cache entry not found"
    // It also implements Unwrap() so the inner error is accessible
    var target interface{ Unwrap() error }
    // In practice, Go toolchain internals use a local check:
    // errors.Is(err, errMissing) or type assertion on *entryNotFoundError
    return err != nil && strings.HasPrefix(err.Error(), "cache entry not found")
}

Try / catch

// All cache read errors (80-89) are wrapped in *entryNotFoundError.
// The canonical pattern (used internally by the Go toolchain) is:

entry, err := cache.Get(actionID)
if err != nil {
    // Treat ANY cache error as a miss — rebuild the output
    // The error message contains the specific reason (too long, file is empty, etc.)
    // but for recovery purposes, all should be handled identically: rebuild.
    output = rebuildOutput()
} else {
    useCachedEntry(entry)
}

Prevention

When it happens

Trigger: DiskCache.get(id) is invoked when looking up any cache entry (via Get, GetFile, GetBytes, GetMmap), and the 'a' (action) file on disk contains at least entrySize+1 bytes — io.ReadFull successfully fills the entire entrySize+1 buffer.

Common situations: A newer Go toolchain version changed the cache entry format; cache directory shared across machines or CI runners with different Go versions; disk corruption or filesystem errors extending the file; antivirus or backup software modifying cache files.

Related errors


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