gohugoio/hugo · critical
invalid cache config: %s
Error message
invalid cache config: %s
What it means
A panic raised in NewCache (cache/filecache/filecache.go:81) when FileCacheConfig.init() returns an error. Currently init() only errors when the Dir still contains ':' placeholders that were not resolved during DecodeConfig. Hugo panics because a half-resolved cache directory would silently corrupt builds.
Source
Thrown at cache/filecache/filecache.go:81
}
// Lock tracks the ids in use. We use this information to do garbage collection
// after a Hugo build.
func (l *lockTracker) Lock(id string) {
l.seen.AddIfAbsent(id)
l.Locker.Lock(id)
}
// ItemInfo contains info about a cached file.
type ItemInfo struct {
// This is the file's name relative to the cache's filesystem.
Name string
}
// NewCache creates a new file cache with the given filesystem and max age.
func NewCache(fs afero.Fs, cfg FileCacheConfig) *Cache {
if err := cfg.init(); err != nil {
panic(fmt.Sprintf("invalid cache config: %s", err))
}
return &Cache{
Fs: fs,
entryLocker: &lockTracker{Locker: locker.NewLocker(), seen: maphelpers.NewConcurrentSet[string]()},
cfg: cfg,
}
}
// lockedFile is a file with a lock that is released on Close.
type lockedFile struct {
afero.File
unlock func()
}
func (l *lockedFile) Close() error {
defer l.unlock()
return l.File.Close()View on GitHub (pinned to 52c9bd7908)
Solutions
- Always run cache configs through filecache.DecodeConfig so placeholders are resolved before NewCache
- Avoid unknown ':' placeholders in cache dir configuration
- In tests, set DirCompiled directly rather than Dir with placeholders
Example fix
// before
c := NewCache(fs, FileCacheConfig{Dir: ":cacheDir/modules"})
// after
cfgs, _ := filecache.DecodeConfig(fs, bcfg, map[string]any{})
c := NewCache(fs, cfgs[filecache.CacheKeyModules]) Defensive patterns
Strategy: validation
Validate before calling
// Resolve placeholders via DecodeConfig before constructing a cache.
if err := cfg.init(); err != nil {
log.Fatal(err)
}
// Or always go through:
// cfgs, err := filecache.DecodeConfig(fs, bcfg, userConfig) Try / catch
// Panics cannot be caught cleanly in production; prefer validation.
// If unavoidable, isolate in a goroutine with recover:
func safeNewCache(fs afero.Fs, cfg FileCacheConfig) (c *Cache, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("NewCache panic: %v", r)
}
}()
return NewCache(fs, cfg), nil
} Prevention
- Always construct caches through filecache.DecodeConfig
- Avoid ':' placeholders other than :cacheDir/:resourceDir/:project
- In tests set DirCompiled directly instead of Dir with placeholders
When it happens
Trigger: Programmatically building a FileCacheConfig with a Dir containing an unresolved ':' placeholder and calling NewCache directly, bypassing DecodeConfig's placeholder resolution.
Common situations: Custom test setups that construct FileCacheConfig by hand; an unknown placeholder (not :cacheDir/:resourceDir/:project) slipping through resolution.
Related errors
- invalid cache dir: %q
- failed to create file caches from configuration: %w
- publishDir is empty
- workingDir is too short
- Unable to locate config file or config directory. Perhaps yo
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/e011dde817c71042.
Report an issue: GitHub.