golangci/golangci-lint · error
failed to gob decode: %w
Error message
failed to gob decode: %w
What it means
internal/cache.Cache.decode wraps any error returned by gob decoding of cached bytes into "failed to gob decode: %w". It is thrown when the stored byte buffer cannot be decoded into the requested value, and it is instrumented via sw.TrackStageErr under the "gob" stage. The caller (Cache.Get) surfaces this when a cache entry exists but is unreadable.
Source
Thrown at internal/cache/cache.go:287
func (c *Cache) encode(data any) (*bytes.Buffer, error) {
buf := &bytes.Buffer{}
err := c.sw.TrackStageErr("gob", func() error {
return gob.NewEncoder(buf).Encode(data)
})
if err != nil {
return nil, fmt.Errorf("failed to gob encode: %w", err)
}
return buf, nil
}
func (c *Cache) decode(b []byte, data any) error {
err := c.sw.TrackStageErr("gob", func() error {
return gob.NewDecoder(bytes.NewReader(b)).Decode(data)
})
if err != nil {
return fmt.Errorf("failed to gob decode: %w", err)
}
return nil
}
func SetSalt(b *bytes.Buffer) {
cache.SetSalt(b.Bytes())
}
func DefaultDir() (string, error) {
cacheDir, _, err := cache.DefaultDir()
return cacheDir, err
}
View on GitHub (pinned to ed7a235d2d)
Solutions
- Delete the stale/corrupt cache directory (e.g. the app's cache command or manually remove the cache dir) so entries are regenerated
- Ensure the type passed to Decode matches the type that was gob-encoded; gob is type-strict about struct names and exported fields
- Bump a cache format/version key so old entries written by incompatible versions are ignored instead of decoded
- Check the wrapped error (%w) for the exact gob failure: 'unexpected EOF' means truncation, 'type mismatch' means schema drift
Example fix
// before
cacheDir, _ := cache.DefaultDir()
_ = os.ReadFile(filepath.Join(cacheDir, key)) // decodes stale v1 gob data
// after
// include format version in cache key so old gob payloads are never decoded
db.Get(filepath.Join("v2", key), &out) // or purge: os.RemoveAll(cacheDir) Defensive patterns
Strategy: fallback
Validate before calling
if len(b) == 0 {
return nil, fmt.Errorf("cache entry empty, skipping decode")
}
// optionally verify a stored format version prefix before decoding
if !bytes.HasPrefix(b, []byte(cacheFormatV2)) {
return nil, fmt.Errorf("cache format version mismatch")
} Type guard
func isGobDecodeError(err error) bool {
return err != nil && strings.Contains(err.Error(), "failed to gob decode")
} Try / catch
if err := cache.Get(ctx, key, &out); err != nil {
if isGobDecodeError(err) {
log.Printf("corrupt/stale cache entry %q: %v — regenerating", key, err)
_ = cache.Invalidate(ctx, key)
out = recompute() // fall back to recomputation
} else {
return err
}
} Prevention
- Include a format/version string in cache keys so entries from incompatible versions are never decoded
- Treat any decode error as a cache miss and recompute rather than failing the operation
- Keep gob-encoded structs' exported field names stable across releases, or bump the version key when they change
- Write cache files atomically (temp file + rename) to avoid truncated entries
When it happens
Trigger: Cache.Get hit an existing entry and decode() called gob.NewDecoder(bytes.NewReader(b)).Decode(data), but the bytes are not valid gob data or do not match the target type.
Common situations: Cache files written by a different version of the app (struct fields/types changed between releases, so gob type names no longer match); corrupted or truncated cache files (crash mid-write, disk issues); manually edited cache files; decoding into a different Go type than was encoded.
Related errors
- failed to remove dir %s: %w
- failed to build packages cache: %w
- failed to init hash salt: %w
- failed to calculate binary salt: %w
- failed to calculate config salt: %w
AI-assisted analysis of golangci/golangci-lint@ed7a235d2d (2026-09-02).
Data as JSON: /api/errors/862971f225b8da39.
Report an issue: GitHub.