{"record":{"id":"795249b400fcf136","repo":"JuliusBrussee/caveman","slug":"report-path-is-required","errorCode":null,"errorMessage":"report path is required","messagePattern":"report path is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/internal/store/report.go","lineNumber":80,"sourceCode":"\thistory := filepath.Join(dir, \"caveman-learn.\"+now.UTC().Format(\"2006-01-02\")+\".json\")\n\tif err := os.WriteFile(history, raw, 0o600); err != nil {\n\t\treturn \"\", err\n\t}\n\tif err := os.Chmod(history, 0o600); err != nil {\n\t\treturn \"\", err\n\t}\n\tentries, _ := filepath.Glob(filepath.Join(dir, \"caveman-learn.????-??-??.json\"))\n\tsort.Strings(entries)\n\tfor len(entries) > 8 {\n\t\t_ = os.Remove(entries[0])\n\t\tentries = entries[1:]\n\t}\n\treturn current, nil\n}\n\nfunc (s *Store) WriteLearnHTML(plan LearnPlan, outPath string) error {\n\tif outPath == \"\" {\n\t\treturn fmt.Errorf(\"report path is required\")\n\t}\n\tif err := os.MkdirAll(filepath.Dir(outPath), 0o700); err != nil {\n\t\treturn err\n\t}\n\tf, err := os.OpenFile(outPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o600)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer f.Close()\n\treturn learnTemplate.Execute(f, struct {\n\t\tPlan      LearnPlan\n\t\tGenerated string\n\t}{Plan: plan, Generated: time.Now().UTC().Format(time.RFC3339)})\n}\n\nfunc (s *Store) WriteTrialHTML(plan TrialPlan, outPath string) error {\n\tif outPath == \"\" {\n\t\treturn fmt.Errorf(\"report path is required\")","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/proxy/internal/store/report.go#L62-L98","documentation":"Returned by Store.WriteLearnHTML when outPath is the empty string. The report writer refuses to guess a destination — it creates the parent directory and writes a 0600 HTML file at exactly the path given, so an empty path is a caller programming error, not an environmental failure.","triggerScenarios":"Calling WriteLearnHTML(plan, \"\") — typically because a --out flag was omitted and its zero-value empty string was passed straight through, or a path-building helper returned \"\" on error and the error was ignored.","commonSituations":"A CLI command where the output flag is optional but the code does not default it; filepath.Join with an empty base producing an unintended empty; refactoring that dropped the path argument's assignment.","solutions":["Default the output path when the flag is empty (e.g. filepath.Join(home, \"learn\", \"index.html\")) or reject it at the CLI layer with a clear 'missing --out' message before calling the store","Fix the caller to derive the path from a known-good base directory","Add a unit test that the command fails fast on a missing out path rather than reaching the store"],"exampleFix":"// before\nerr := st.WriteLearnHTML(plan, outPath) // outPath == \"\" when --out omitted\n\n// after\nif outPath == \"\" {\n    return fmt.Errorf(\"missing --out: learn HTML destination is required\")\n}\nerr := st.WriteLearnHTML(plan, outPath)","handlingStrategy":"validation","validationCode":"func requireOutPath(p string) (string, error) {\n    p = strings.TrimSpace(p)\n    if p == \"\" { return \"\", errors.New(\"--out is required for learn HTML\") }\n    return p, nil\n}","typeGuard":"func hasOutPath(p string) bool { return strings.TrimSpace(p) != \"\" }","tryCatchPattern":"if err := st.WriteLearnHTML(plan, outPath); err != nil {\n    if strings.Contains(err.Error(), \"report path is required\") {\n        return usageError(\"missing --out\") // surface a CLI usage message, not a raw store error\n    }\n    return err\n}","preventionTips":["Validate output flags at the CLI boundary with usage errors","Default report paths deterministically under home when the flag is optional","Never ignore an error from a path-derivation helper that returns \"\""],"tags":["validation","cli","api-misuse","go"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}