{"record":{"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/52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2/cache/filecache/filecache_config.go#L109-L145","documentation":"In FileCacheConfig.init (filecache_config.go:123-128), when DirCompiled is empty but the raw Dir string contains a colon, it means a placeholder like :cacheDir was never resolved. This guard is primarily reached from unit tests that construct FileCacheConfig directly without running the full DecodeConfig pipeline.","triggerScenarios":"Constructing a FileCacheConfig in Go code/tests with Dir set to \":cacheDir/foo\" but never calling DecodeConfig to resolve placeholders into DirCompiled.","commonSituations":"Unit tests of the filecache package that bypass config decoding; custom code that instantiates FileCacheConfig directly.","solutions":["Run the config through DecodeConfig to resolve placeholders before use","Set DirCompiled explicitly if intentionally bypassing the decode pipeline","Avoid colons in raw directory names used in test fixtures"],"exampleFix":"// before\ncfg := FileCacheConfig{Dir: \":cacheDir/modules\"}\ncfg.init() // error: unresolved placeholder\n// after\ncfg.DirCompiled = \"/tmp/hugo-cache/modules\"\ncfg.init()","handlingStrategy":"validation","validationCode":"// In test code, resolve placeholders or set DirCompiled before calling init.\nfunc mustResolveDir(t *testing.T, cfg *FileCacheConfig, baseCacheDir string) {\n    t.Helper()\n    if cfg.DirCompiled == \"\" && strings.Contains(cfg.Dir, \":\") {\n        cfg.DirCompiled = filepath.Join(baseCacheDir, strings.TrimPrefix(cfg.Dir, \":cacheDir\"))\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["In tests, always run config through DecodeConfig rather than constructing FileCacheConfig directly","Set DirCompiled explicitly when bypassing the decode pipeline","Avoid colon characters in literal test directory names"],"tags":["cache","filecache","config","testing","placeholder","hugo"],"backgroundTag":null,"analyzedSha":"52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2","analyzedAt":"2026-08-09T21:49:36.660Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}