hashicorp/terraform · error

cannot search %s: %s

Error message

cannot search %s: %s

What it means

Raised inside the filepath.Walk callback in SearchLocalDirectory (internal/getproviders/filesystem_search.go:51). When the walk cannot access a path (the 'err' argument to the visit function is non-nil), it is wrapped as 'cannot search <path>: <err>'. Causes include permission denied, a broken/dangling symlink in the hierarchy, or the directory disappearing mid-walk. The error aborts the whole scan.

Source

Thrown at internal/getproviders/filesystem_search.go:51

	// potentially keep their plugins in a non-standard location but use a
	// symlink to help Terraform find them anyway.
	originalBaseDir := baseDir
	if finalDir, err := filepath.EvalSymlinks(baseDir); err == nil {
		if finalDir != filepath.Clean(baseDir) {
			log.Printf("[TRACE] getproviders.SearchLocalDirectory: using %s instead of %s", finalDir, baseDir)
		}
		baseDir = finalDir
	} else {
		// We'll eat this particular error because if we're somehow able to
		// find plugins via baseDir below anyway then we'd rather do that than
		// hard fail, but we'll log it in case it's useful for diagnosing why
		// discovery didn't produce the expected outcome.
		log.Printf("[TRACE] getproviders.SearchLocalDirectory: failed to resolve symlinks for %s: %s", baseDir, err)
	}

	err := filepath.Walk(baseDir, func(fullPath string, info os.FileInfo, err error) error {
		if err != nil {
			return fmt.Errorf("cannot search %s: %s", fullPath, err)
		}

		// There are two valid directory structures that we support here...
		// Unpacked: registry.terraform.io/hashicorp/aws/2.0.0/linux_amd64 (a directory)
		// Packed:   registry.terraform.io/hashicorp/aws/terraform-provider-aws_2.0.0_linux_amd64.zip (a file)
		//
		// Both of these give us enough information to identify the package
		// metadata.
		fsPath, err := filepath.Rel(baseDir, fullPath)
		if err != nil {
			// This should never happen because the filepath.Walk contract is
			// for the paths to include the base path.
			log.Printf("[TRACE] getproviders.SearchLocalDirectory: ignoring malformed path %q during walk: %s", fullPath, err)
			return nil
		}
		relPath := filepath.ToSlash(fsPath)
		parts := strings.Split(relPath, "/")

View on GitHub (pinned to c9def3e214)

Solutions

  1. Fix filesystem permissions so the Terraform process can read the entire mirror tree (e.g. chmod -R +rX on the mirror directory).
  2. Find and remove or repair dangling symlinks in the mirror hierarchy.
  3. Confirm the mirror directory path configured in the CLI config actually exists and is stable during the scan.
  4. If a path is intentionally inaccessible, exclude it or move the mirror to a clean location.

Example fix

# before: mirror dir not readable by terraform process
# after
chmod -R a+rX /opt/terraform-plugins
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify the mirror directory is walkable before scanning.
func walkable(dir string) error {
	info, err := os.Stat(dir)
	if err != nil {
		return err
	}
	if !info.IsDir() {
		return fmt.Errorf("%s is not a directory", dir)
	}
	// Attempt a representative read to surface permission errors early.
	f, err := os.Open(dir)
	if err != nil {
		return err
	}
	f.Close()
	return nil
}

Try / catch

found, err := getproviders.SearchLocalDirectory(mirrorDir)
if err != nil {
    return fmt.Errorf("cannot scan provider mirror %s (check permissions/symlinks): %w", mirrorDir, err)
}

Prevention

When it happens

Trigger: Scanning a filesystem provider mirror directory that contains a path Go's walker cannot stat or read: a dangling symlink, a permission-restricted subdirectory, or a path deleted during the walk. Triggered by any terraform init / provider discovery that uses a local mirror.

Common situations: A plugin filesystem mirror (CLI plugin_cache_dir or filesystem_mirror) with restrictive permissions; a broken symlink left by a failed download; a network mount that dropped mid-scan.

Related errors


AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07). Data as JSON: /api/errors/6b16611a4ac1e9c1. Report an issue: GitHub.