JuliusBrussee/caveman · error

repository intelligence: cwd is not a directory

Error message

repository intelligence: cwd is not a directory

What it means

repointel.Build resolves the root path through symlink evaluation and absolutization, then stats it. If stat fails or the result is not a directory, it reports that the cwd/repository root is not a directory. Note the error also masks the underlying stat error, so permission problems surface the same way.

Source

Thrown at proxy/internal/repointel/index.go:104

	ChangedPaths   []string `json:"changed_paths"`
	AffectedTests  []string `json:"affected_tests"`
	Basis          string   `json:"basis"`
	EvidenceStatus string   `json:"evidence_status"`
	Conservative   bool     `json:"conservative"`
}

func Build(ctx context.Context, root, repositoryState string, queryTerms []string) (Map, Bundle, error) {
	resolved, err := filepath.EvalSymlinks(root)
	if err != nil {
		return Map{}, Bundle{}, err
	}
	resolved, err = filepath.Abs(resolved)
	if err != nil {
		return Map{}, Bundle{}, err
	}
	info, err := os.Stat(resolved)
	if err != nil || !info.IsDir() {
		return Map{}, Bundle{}, errors.New("repository intelligence: cwd is not a directory")
	}
	files, truncated, err := listFiles(ctx, resolved)
	if err != nil {
		return Map{}, Bundle{}, err
	}
	packages := packageBoundaries(files)
	activity := gitActivity(ctx, resolved)
	mapped := make([]File, 0, len(files))
	for _, relative := range files {
		entry := File{
			Path: relative, Language: languageFor(relative), Package: nearestPackage(relative, packages),
			TestFor: testTarget(relative, files), RecentChanges: activity[relative], Convention: isConvention(relative),
		}
		if entry.Language != "" && !sensitivePath(relative) {
			path := filepath.Join(resolved, filepath.FromSlash(relative))
			if raw, readErr := os.ReadFile(path); readErr == nil && len(raw) <= maxFileBytes && !containsNUL(raw) {
				entry.Imports = scanImports(entry.Language, raw)
				entry.Symbols = scanSymbols(ctx, relative, entry.Language, raw)

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Pass the repository root directory, not any file inside it
  2. Verify the path exists and is a directory before calling Build (os.Stat + IsDir)
  3. If permissions are suspect, check the resolved path's modes and the mount state

Example fix

// before
map, bundle, err := repointel.Build(ctx, "repo/go.mod", state, terms)

// after
info, err := os.Stat(root)
if err != nil || !info.IsDir() {
    return fmt.Errorf("root %q is not a directory", root)
}
map, bundle, err := repointel.Build(ctx, root, state, terms)
Defensive patterns

Strategy: validation

Validate before calling

info, err := os.Stat(root)
if err != nil || !info.IsDir() {
    return fmt.Errorf("root %q is not a directory", root)
}
mp, bundle, err := repointel.Build(ctx, root, state, terms)

Type guard

func isDir(p string) bool {
    info, err := os.Stat(p)
    return err == nil && info.IsDir()
}

Prevention

When it happens

Trigger: Calling Build with a path that is a file (e.g. go.mod, a symlink to a file), a deleted directory, or a directory the process cannot stat due to permissions.

Common situations: Passing the repo's manifest file instead of its root; root resolved from config pointing at a mounted/unmounted volume; symlink chains that EvalSymlinks resolves to a file; sandboxed environments denying stat.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/4b70225b7b7898fa. Report an issue: GitHub.