{"id":"b7a691c1f0476058","repo":"gohugoio/hugo","slug":"cache-dir-q-contains-unresolved-placeholders","errorCode":null,"errorMessage":"cache dir %q contains unresolved placeholders","messagePattern":"cache dir %q contains unresolved placeholders","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cache/filecache/filecache_config.go","lineNumber":127,"sourceCode":"\t// be removed and not returned from the cache.\n\t// A negative value means forever, 0 means cache is disabled.\n\t// Hugo is lenient with what types it accepts here, but we recommend using\n\t// a duration string, a sequence of  decimal numbers, each with optional fraction and a unit suffix,\n\t// such as \"300ms\", \"1.5h\" or \"2h45m\".\n\t// Valid time units are \"ns\", \"us\" (or \"µs\"), \"ms\", \"s\", \"m\", \"h\".\n\tMaxAge time.Duration\n\n\t// The directory where files are stored.\n\tDir string\n\n\tfileCacheConfigInternal `json:\"-\"`\n}\n\nfunc (cfg *FileCacheConfig) init() error {\n\tif cfg.DirCompiled == \"\" {\n\t\t// From unit tests. Just check that it does not contain any placeholders.\n\t\tif strings.Contains(cfg.Dir, \":\") {\n\t\t\treturn fmt.Errorf(\"cache dir %q contains unresolved placeholders\", cfg.Dir)\n\t\t}\n\t\tcfg.DirCompiled = cfg.Dir\n\t}\n\t// Sanity check the config.\n\tif len(cfg.DirCompiled) < 5 {\n\t\tpanic(fmt.Sprintf(\"invalid cache dir: %q\", cfg.DirCompiled))\n\t}\n\treturn nil\n}\n\ntype fileCacheConfigInternal struct {\n\tDirCompiled string\n\n\tname          string // The name of this cache, e.g. \"images\", \"modules\" etc.\n\tentryIsDir    bool   // when set, the cache entries represents directories directly below the base dir.\n\tisReadOnly    bool   // when set, the cache is read only and needs to be pruned differently. This is used for the Go modules cache.\n\tIsResourceDir bool   //  resources/_gen will get its own composite filesystem that also checks any theme. TODO(bep) unexport this.\n}","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/gohugoio/hugo/blob/8a468df065a75c1c7cf9f6850f32148746590ea5/cache/filecache/filecache_config.go#L109-L145","documentation":"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.","triggerScenarios":"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'.","commonSituations":"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.","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."],"exampleFix":"// before (test code)\ncfg := filecache.FileCacheConfig{Dir: \":cacheDir/images\"}\ncfg.init() // error\n// after\ncfg := filecache.FileCacheConfig{Dir: \"/tmp/hugo_cache/images\"}\ncfg.init()","handlingStrategy":"validation","validationCode":"// Before building config, verify resolved dir has no leftover placeholders\nif strings.Contains(resolvedDir, \":\") {\n    return fmt.Errorf(\"cache dir %q still contains placeholders\", resolvedDir)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["hugo","filecache","config","placeholder"],"analyzedSha":"8a468df065a75c1c7cf9f6850f32148746590ea5","analyzedAt":"2026-07-31T21:23:07.045Z","schemaVersion":2}