charmbracelet/crush · error

failed to load projects: %w

Error message

failed to load projects: %w

What it means

When `crush stats --all` is used, stats are gathered from every project listed in projects.json (next to the global config). This error wraps a failure from projects.Load(), which can only happen if projects.json exists but is unreadable or is not valid JSON.

Source

Thrown at internal/cmd/stats.go:317

		"logs":             true,
		"generated":        true,
		"bower_components": true,
		"jspm_packages":    true,
		".cache":           true,
		".npm":             true,
		".cargo":           true,
		"Library":          true,
		"Applications":     true,
		"System":           true,
	}
	return skipDirs[name]
}

// gatherStatsFromProjects gathers stats from all known projects in projects.json.
func gatherStatsFromProjects(ctx context.Context) ([]ProjectStats, error) {
	projectList, err := projects.Load()
	if err != nil {
		return nil, fmt.Errorf("failed to load projects: %w", err)
	}

	var dbPaths []struct {
		dbPath     string
		projectDir string
	}

	for _, p := range projectList.Projects {
		dbPath := filepath.Join(p.DataDir, "crush.db")
		if _, err := os.Stat(dbPath); err == nil {
			dbPaths = append(dbPaths, struct {
				dbPath     string
				projectDir string
			}{dbPath: dbPath, projectDir: p.Path})
		}
	}

	return gatherStatsFromDBPaths(ctx, dbPaths)

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Validate the file: `cat $(dirname $(crush config path))/projects.json | jq .` — fix or remove broken JSON
  2. If the file is corrupt and not needed, delete it; projects.Load() recreates an empty list when it is absent
  3. Fix permissions on the file so the current user can read it (chown/chmod)

Example fix

// before (corrupt projects.json)
{"projects": [ {"path": "/a",  // truncated
// after
rm projects.json  # or repair with jq; Load() then returns an empty project list
Defensive patterns

Strategy: validation

Validate before calling

pj="$(dirname "$(crush config path 2>/dev/null)")/projects.json"
if [ -f "$pj" ] && ! jq empty "$pj" 2>/dev/null; then
  echo "projects.json is invalid JSON; repair or remove it: $pj" >&2
  exit 1
fi

Try / catch

if err := runStats(...); err != nil {
    var syntaxErr *json.SyntaxError
    if errors.As(err, &syntaxErr) {
        fmt.Fprintf(os.Stderr, "projects.json corrupt at offset %d; delete it to reset\n", syntaxErr.Offset)
        return nil
    }
    return err
}

Prevention

When it happens

Trigger: Running `crush stats` with the all-projects flag when projects.json cannot be read (permission denied) or json.Unmarshal fails because the file is malformed/hand-edited incorrectly.

Common situations: Manually editing ~/.config/crush/projects.json and breaking the JSON syntax; file owned by root after running crush with sudo once; disk corruption; truncation from an interrupted write.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


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