golang/go · error · fs.PathError

overlay maps child %s to directory

Error message

overlay maps child %s to directory

What it means

During a ReadDir operation on the overlay filesystem, a child entry's replacement target (the 'actual' path on disk) is itself a directory. The overlay data model only supports file-to-file mappings — directory targets are explicitly rejected to avoid ambiguity between Lstat and Stat interactions with directories.

Source

Thrown at src/cmd/go/internal/fsys/fsys.go:485

	for cname, cinfo := range info.children() {
		if cinfo.dir {
			all[cname] = fs.FileInfoToDirEntry(fakeDir(cname))
			continue
		}
		if cinfo.deleted {
			delete(all, cname)
			continue
		}

		// Overlay is not allowed to have targets that are directories.
		// And we hide symlinks, although it's not clear it helps callers.
		cinfo, err := os.Stat(cinfo.actual)
		if err != nil {
			all[cname] = fs.FileInfoToDirEntry(missingFile(cname))
			continue
		}
		if cinfo.IsDir() {
			return nil, &fs.PathError{Op: "read", Path: name, Err: fmt.Errorf("overlay maps child %s to directory", cname)}
		}
		all[cname] = fs.FileInfoToDirEntry(fakeFile{cname, cinfo})
	}

	// Rebuild list using same storage.
	dirs = dirs[:0]
	for _, d := range all {
		dirs = append(dirs, d)
	}
	slices.SortFunc(dirs, func(x, y fs.DirEntry) int { return strings.Compare(x.Name(), y.Name()) })

	if len(dirs) == 0 {
		return nil, dirErr
	}
	return dirs, nil
}

// Actual returns the actual file system path for the named file.

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Check the overlay JSON entry for the child path named in the error message — its target must be a regular file.
  2. Replace the directory target with a specific file path (e.g., '/real/src' -> '/real/src/main.go').
  3. If you need to overlay an entire directory tree, enumerate each file individually in the Replace map.
  4. Remove the overlay entry if the path should remain a directory on disk.

Example fix

// before — overlay target is a directory
{
  "Replace": {
    "/proj/pkg/main.go": "/real/pkg"
  }
}
// after — overlay target is a file
{
  "Replace": {
    "/proj/pkg/main.go": "/real/pkg/main.go"
  }
}
Defensive patterns

Strategy: validation

Validate before calling

// Check that all overlay targets are regular files, not directories.
func validateOverlayTargets(entries map[string]string) error {
    for from, to := range entries {
        if to == "" {
            continue
        }
        info, err := os.Stat(to)
        if err != nil {
            continue
        }
        if info.IsDir() {
            return fmt.Errorf("overlay target %s -> %s is a directory", from, to)
        }
    }
    return nil
}

Prevention

When it happens

Trigger: An overlay JSON Replace map entry points to a real path that is a directory. When go list, go build, or any tooling enumerates directory contents via fsys.ReadDir, it calls os.Stat on each child's 'actual' path; if that returns IsDir()==true, this error fires. The cname in the message is the virtual child path.

Common situations: Manually creating an overlay JSON and pointing a replacement target at a directory instead of a specific file. Misunderstanding that the overlay replaces individual files, not directory trees. Symlink targets in the overlay resolving to directories.

Related errors


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/b83b6e6cc6b02996. Report an issue: GitHub.