gohugoio/hugo · error
cache dir %q contains unresolved placeholders
Error message
cache dir %q contains unresolved placeholders
What it means
Raised by FileCacheConfig.init in Hugo's filecache package when the compiled cache directory (DirCompiled) is empty and the raw Dir string still contains a ':' character, indicating placeholder tokens like :cacheDir or :resourceDir were never resolved by DecodeConfig. Hugo refuses to use such a path because writing cache files to a literal ':cacheDir/...' path would create bogus directories. This path is mostly hit when a FileCacheConfig is constructed programmatically (e.g. in unit tests) and init() runs without the normal DecodeConfig placeholder-resolution pass.
Source
Thrown at cache/filecache/filecache_config.go:127
// be removed and not returned from the cache.
// A negative value means forever, 0 means cache is disabled.
// Hugo is lenient with what types it accepts here, but we recommend using
// a duration string, a sequence of decimal numbers, each with optional fraction and a unit suffix,
// such as "300ms", "1.5h" or "2h45m".
// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
MaxAge time.Duration
// The directory where files are stored.
Dir string
fileCacheConfigInternal `json:"-"`
}
func (cfg *FileCacheConfig) init() error {
if cfg.DirCompiled == "" {
// From unit tests. Just check that it does not contain any placeholders.
if strings.Contains(cfg.Dir, ":") {
return fmt.Errorf("cache dir %q contains unresolved placeholders", cfg.Dir)
}
cfg.DirCompiled = cfg.Dir
}
// Sanity check the config.
if len(cfg.DirCompiled) < 5 {
panic(fmt.Sprintf("invalid cache dir: %q", cfg.DirCompiled))
}
return nil
}
type fileCacheConfigInternal struct {
DirCompiled string
name string // The name of this cache, e.g. "images", "modules" etc.
entryIsDir bool // when set, the cache entries represents directories directly below the base dir.
isReadOnly bool // when set, the cache is read only and needs to be pruned differently. This is used for the Go modules cache.
IsResourceDir bool // resources/_gen will get its own composite filesystem that also checks any theme. TODO(bep) unexport this.
}View on GitHub ↗ (pinned to 8a468df065)
Solutions
- Ensure cache configs go through filecache.DecodeConfig so :cacheDir/:resourceDir placeholders are resolved into DirCompiled before init() runs.
- In tests, set Dir to a concrete absolute path (no ':' placeholders) or set DirCompiled explicitly.
- Check caches.*.dir in your site config for typos like a stray colon inside the path.
Example fix
// before (test code)
cfg := filecache.FileCacheConfig{Dir: ":cacheDir/images"}
cfg.init() // error
// after
cfg := filecache.FileCacheConfig{Dir: "/tmp/hugo_cache/images"}
cfg.init() Defensive patterns
Strategy: validation
Validate before calling
// Before building config, verify resolved dir has no leftover placeholders
if strings.Contains(resolvedDir, ":") {
return fmt.Errorf("cache dir %q still contains placeholders", resolvedDir)
} Prevention
- Only use the documented placeholders :cacheDir and :resourceDir in caches.*.dir
- Run hugo config to inspect the resolved caches section before builds
- Don't concatenate custom tokens into cache dir paths; keep them literal or placeholder-prefixed
When it happens
Trigger: Calling FileCacheConfig.init() with DirCompiled unset and Dir containing ':' — e.g. constructing filecache configs directly in tests or embedding code without running filecache.DecodeConfig first; or a config path where placeholder substitution was skipped so Dir is still ':cacheDir/images'.
Common situations: Unit tests or tools that build Hugo config objects manually instead of going through the full config decode; custom integrations of the filecache package; a Windows path with a drive-letter colon in an unexpected position combined with a code path that bypasses placeholder resolution.
Related errors
- %q is not a valid placeholder (valid values are :cacheDir or
- failed to create file caches from configuration: %w
- %q is not a valid cache name
- must provide cache Dir
- %q must resolve to an absolute directory
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/b7a691c1f0476058.json.
Report an issue: GitHub ↗.