{"record":{"id":"4a43b0b917c90feb","repo":"hashicorp/terraform","slug":"global-cache-directory-s-must-not-match-the-insta","errorCode":null,"errorMessage":"global cache directory %s must not match the installation target directory %s","messagePattern":"global cache directory (.+?) must not match the installation target directory (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"internal/providercache/installer.go","lineNumber":118,"sourceCode":"// ProviderSource returns the getproviders.Source that the installer would\n// use for installing any new providers.\nfunc (i *Installer) ProviderSource() getproviders.Source {\n\treturn i.source\n}\n\n// SetGlobalCacheDir activates a second tier of caching for the receiving\n// installer, with the given directory used as a read-through cache for\n// installation operations that need to retrieve new packages.\n//\n// The global cache directory for an installer must never be the same as its\n// target directory, and must not be used as one of its provider sources.\n// If these overlap then undefined behavior will result.\nfunc (i *Installer) SetGlobalCacheDir(cacheDir *Dir) {\n\t// A little safety check to catch straightforward mistakes where the\n\t// directories overlap. Better to panic early than to do\n\t// possibly-distructive actions on the cache directory downstream.\n\tif same, err := copydir.SameFile(i.targetDir.baseDir, cacheDir.baseDir); err == nil && same {\n\t\tpanic(fmt.Sprintf(\"global cache directory %s must not match the installation target directory %s\", cacheDir.baseDir, i.targetDir.baseDir))\n\t}\n\ti.globalCacheDir = cacheDir\n}\n\n// SetGlobalCacheDirMayBreakDependencyLockFile activates or deactivates our\n// temporary exception to the rule that the global cache directory can be used\n// only when entries are confirmed by existing entries in the dependency lock\n// file.\n//\n// If this is set then if we install a provider for the first time from the\n// cache then the dependency lock file will include only the checksum from\n// the package in the global cache, which means the lock file won't be portable\n// to Terraform running on another operating system or CPU architecture.\nfunc (i *Installer) SetGlobalCacheDirMayBreakDependencyLockFile(mayBreak bool) {\n\ti.globalCacheDirMayBreakDependencyLockFile = mayBreak\n}\n\n// HasGlobalCacheDir returns true if someone has previously called","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/hashicorp/terraform/blob/c9def3e214014c1188faabfc4a5bde5095139765/internal/providercache/installer.go#L100-L136","documentation":"Installer.SetGlobalCacheDir panics when the global cache directory resolves to the same filesystem path as the installer's target directory (internal/providercache/installer.go:118). This is a deliberate early guard: if the read-through cache and the install target overlap, downstream install/copy operations would recurse and potentially destroy the cache contents. The check uses copydir.SameFile and only panics when both paths are stat-identical and no error occurred.","triggerScenarios":"Calling installer.SetGlobalCacheDir(cacheDir) where cacheDir.baseDir and installer.targetDir.baseDir point to the same directory (e.g. both constructed from the same path string or symlink-resolved to the same inode). Common when a CLI flag/env var feeds both the plugin install target and the shared cache location.","commonSituations":"Setting TF_PLUGIN_CACHE_DIR to the same value as the managed plugin directory (e.g. .terraform/providers); CLI wrappers or wrappers like tfenv/tenv that reuse one directory for both roles; symlinked paths that resolve to the same real directory; cloning an installer and forgetting to override the target before reassigning the cache.","solutions":["Point the global cache directory at a different path than the installation target (e.g. keep TF_PLUGIN_CACHE_DIR separate from .terraform/providers).","Before calling SetGlobalCacheDir, verify with copydir.SameFile(target.baseDir, cache.baseDir) and skip or error instead of panicking.","Resolve symlinks for both paths (filepath.EvalSymlinks) and compare to catch indirect collisions.","If cloning an Installer via Clone(targetDir), ensure the new targetDir is distinct from the inherited globalCacheDir before any reconfiguration."],"exampleFix":"// before\ninstaller.SetGlobalCacheDir(targetDir) // same dir as install target -> panic\n\n// after\nif same, _ := copydir.SameFile(targetDir.baseDir, cacheDir.baseDir); same {\n    return fmt.Errorf(\"cache dir %s must differ from target %s\", cacheDir.baseDir, targetDir.baseDir)\n}\ninstaller.SetGlobalCacheDir(cacheDir) // distinct dir","handlingStrategy":"validation","validationCode":"import (\n    \"fmt\"\n    \"github.com/hashicorp/terraform/internal/copydir\"\n)\nfunc safeSetGlobalCacheDir(i *providercache.Installer, cacheDir *providercache.Dir) error {\n    td := i.TargetDir() // assumes a getter exposes targetDir.baseDir\n    if same, err := copydir.SameFile(td, cacheDir.baseDir); err == nil && same {\n        return fmt.Errorf(\"global cache dir %s must differ from target %s\", cacheDir.baseDir, td)\n    }\n    i.SetGlobalCacheDir(cacheDir)\n    return nil\n}","typeGuard":"func dirsAreDistinct(a, b string) (bool, error) {\n    ra, err := filepath.EvalSymlinks(a)\n    if err != nil { return false, err }\n    rb, err := filepath.EvalSymlinks(b)\n    if err != nil { return false, err }\n    same, err := copydir.SameFile(ra, rb)\n    return !same, err\n}","tryCatchPattern":"defer func() {\n    if r := recover(); r != nil {\n        err = fmt.Errorf(\"SetGlobalCacheDir rejected overlapping dirs: %v\", r)\n    }\n}()\ni.SetGlobalCacheDir(cacheDir)","preventionTips":["Keep TF_PLUGIN_CACHE_DIR and the managed plugin target (.terraform/providers) on distinct paths.","Resolve symlinks before comparing, since SameFile follows to the same inode.","After Installer.Clone(targetDir), confirm the new target differs from the inherited cache dir.","Wrap SetGlobalCacheDir in a helper that validates distinctness up front."],"tags":["terraform","providercache","installer","filesystem","configuration","panic"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T21:17:07.882Z"}