charmbracelet/crush · error

failed to crawl for stats: %w

Error message

failed to crawl for stats: %w

What it means

`crush stats --crawl-dir <dir>` walks the given directory tree to compute project statistics via `crawlForStats`. This error wraps any failure of that crawl — unreadable directories, permission errors, or internal crawl failures — as `failed to crawl for stats: <cause>`. The actionable detail is always in the wrapped cause.

Source

Thrown at internal/cmd/stats.go:151

type ProjectStats struct {
	ProjectPath string `json:"project_path"`
	Stats       *Stats `json:"stats"`
}

func runStats(cmd *cobra.Command, _ []string) error {
	ctx := cmd.Context()
	dataDir, _ := cmd.Flags().GetString("data-dir")
	crawlDir, _ := cmd.Flags().GetString("crawl-dir")
	useAll, _ := cmd.Flags().GetBool("all")

	var projectStats []ProjectStats
	var err error

	switch {
	case crawlDir != "":
		projectStats, err = crawlForStats(ctx, crawlDir)
		if err != nil {
			return fmt.Errorf("failed to crawl for stats: %w", err)
		}
	case useAll:
		projectStats, err = gatherStatsFromProjects(ctx)
		if err != nil {
			return fmt.Errorf("failed to gather stats from projects: %w", err)
		}
	default:
		cfg, err := config.Init("", dataDir, false)
		if err != nil {
			return fmt.Errorf("failed to initialize config: %w", err)
		}
		if dataDir == "" {
			dataDir = cfg.Config().Options.DataDirectory
		}
		if shouldEnableMetrics(cfg.Config()) {
			event.Init()
		}

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Read the wrapped cause after the colon to see which path/operation failed.
  2. Verify the --crawl-dir path exists and is a directory: `ls -ld <dir>`.
  3. Check read/execute permissions on the directory tree (`find <dir> -not -readable`).
  4. Re-run pointing at a valid, accessible directory, or omit --crawl-dir to use the default local data directory.

Example fix

// before
crush stats --crawl-dir ./projct  # typo
// after
crush stats --crawl-dir ./project
Defensive patterns

Strategy: validation

Validate before calling

dir := "./project"
info, err := os.Stat(dir)
if err != nil || !info.IsDir() {
    log.Fatalf("--crawl-dir %s is not an existing directory", dir)
}
if f, err := os.Open(dir); err != nil {
    log.Fatalf("--crawl-dir %s not readable: %v", dir, err)
} else {
    f.Close()
}

Type guard

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

Try / catch

out, err := exec.Command("crush", "stats", "--crawl-dir", dir).CombinedOutput()
if err != nil {
    if strings.Contains(string(out), "failed to crawl for stats") {
        log.Fatalf("crawl failed for %s: %s", dir, out)
    }
    log.Fatalf("stats failed: %v: %s", err, out)
}

Prevention

When it happens

Trigger: Running `crush stats` with a non-empty `--crawl-dir` flag where `crawlForStats(ctx, crawlDir)` at internal/cmd/stats.go:149 returns an error: the directory does not exist, is not readable, or the walker hits an unrecoverable I/O error while traversing.

Common situations: Typo in the --crawl-dir path; pointing at a file instead of a directory; crawling a directory with restricted permissions (e.g. another user's home); crawling a network mount that drops mid-scan.

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/c4f7e1962344f010. Report an issue: GitHub.