{"record":{"id":"b3374654fa92de6d","repo":"charmbracelet/crush","slug":"failed-to-generate-html-w","errorCode":null,"errorMessage":"failed to generate HTML: %w","messagePattern":"failed to generate HTML: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cmd/stats.go","lineNumber":230,"sourceCode":"\t\t\treturn fmt.Errorf(\"failed to get current directory: %w\", err)\n\t\t}\n\t\tprojName = strings.Replace(project, currentUser.HomeDir, \"~\", 1)\n\t}\n\n\toutputDataDir := dataDir\n\tif outputDataDir == \"\" {\n\t\tcfg, err := config.Init(\"\", \"\", false)\n\t\tif err == nil {\n\t\t\toutputDataDir = cfg.Config().Options.DataDirectory\n\t\t}\n\t}\n\tif outputDataDir == \"\" {\n\t\toutputDataDir = \".crush\"\n\t}\n\n\thtmlPath := filepath.Join(outputDataDir, \"stats/index.html\")\n\tif err := generateHTML(mergedStats, projectStats, projName, username, htmlPath); err != nil {\n\t\treturn fmt.Errorf(\"failed to generate HTML: %w\", err)\n\t}\n\n\tfmt.Printf(\"Stats generated: %s\\n\", htmlPath)\n\n\tif err := browser.OpenFile(htmlPath); err != nil {\n\t\tfmt.Printf(\"Could not open browser: %v\\n\", err)\n\t\tfmt.Println(\"Please open the file manually.\")\n\t}\n\n\treturn nil\n}\n\n// crawlForStats crawls a directory recursively looking for .crush/crush.db files.\nfunc crawlForStats(ctx context.Context, rootDir string) ([]ProjectStats, error) {\n\tvar dbPaths []struct {\n\t\tdbPath     string\n\t\tprojectDir string\n\t}","sourceCodeStart":212,"sourceCodeEnd":248,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/cmd/stats.go#L212-L248","documentation":"`crush stats` renders the gathered statistics into an HTML report at <dataDir>/stats/index.html. This error wraps any failure inside generateHTML (template execution or file creation/write). The wrapped error tells whether it was a template rendering issue or an I/O problem.","triggerScenarios":"generateHTML fails to create or write the output file (bad permissions, read-only filesystem, <dataDir>/stats not creatable) or fails while executing the HTML template against mergedStats/projectStats.","commonSituations":".crush directory on a read-only mount or owned by another user; disk full; data dir overridden via --data-dir to a path without write permission; corrupted/nil stats triggering template nil-pointer issues.","solutions":["Check that the data dir (default .crush) is writable: `ls -ld .crush` and fix permissions","Verify free disk space with `df -h`","If using a custom --data-dir, ensure the path exists or can be created and is writable","Run `crush stats` from a directory where you own .crush (fix ownership with chown)"],"exampleFix":"// before\nmkdir /protected && crush stats --data-dir /protected/out  # permission denied\n// after\nmkdir -p ~/crush-stats && crush stats --data-dir ~/crush-stats","handlingStrategy":"try-catch","validationCode":"datadir=\"${CRUSH_DATA_DIR:-.crush}\"\nif [ ! -w \"$datadir\" ]; then\n  echo \"Data dir $datadir is not writable\" >&2\n  exit 1\nfi\nif [ \"$(df -h . | awk 'NR==2{print $5}' | tr -d '%')\" -ge 99 ]; then\n  echo \"Disk full; free space before generating stats\" >&2\n  exit 1\nfi","typeGuard":null,"tryCatchPattern":"if err := runStats(...); err != nil {\n    if errors.Is(err, os.ErrPermission) {\n        fmt.Fprintln(os.Stderr, \"Fix permissions on the stats data dir and retry\")\n        return nil\n    }\n    return err // includes template-execution errors via %w\n}","preventionTips":["Ensure the data dir (default .crush) exists and is writable by the running user","Don't run crush via sudo in a way that leaves root-owned .crush files","Monitor disk space where the stats HTML is written","When using a custom --data-dir, pre-create it with correct permissions","Upgrade crush if template nil-pointer errors persist after data changes"],"tags":["filesystem","html","io","cli"],"backgroundTag":"file-write-permission-denied","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}