{"record":{"id":"dd6725c0eca20f5d","repo":"JuliusBrussee/caveman","slug":"export-directory-is-required","errorCode":null,"errorMessage":"export directory is required","messagePattern":"export directory is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/internal/store/report.go","lineNumber":116,"sourceCode":"\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 trialTemplate.Execute(f, struct {\n\t\tPlan      TrialPlan\n\t\tGenerated string\n\t}{Plan: plan, Generated: time.Now().UTC().Format(time.RFC3339)})\n}\n\nfunc (s *Store) ExportTrial(plan TrialPlan, outDir string) (map[string]string, error) {\n\tif outDir == \"\" {\n\t\treturn nil, fmt.Errorf(\"export directory is required\")\n\t}\n\tif err := os.MkdirAll(outDir, 0o700); err != nil {\n\t\treturn nil, err\n\t}\n\ttrialPath := filepath.Join(outDir, \"trial.json\")\n\traw, _ := json.MarshalIndent(plan, \"\", \"  \")\n\tif err := os.WriteFile(trialPath, append(raw, '\\n'), 0o600); err != nil {\n\t\treturn nil, err\n\t}\n\tspansPath := filepath.Join(outDir, \"spans.jsonl\")\n\tf, err := os.OpenFile(spansPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o600)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif err := s.writeExportSpans(f, plan); err != nil {\n\t\t_ = f.Close()\n\t\treturn nil, err\n\t}","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/proxy/internal/store/report.go#L98-L134","documentation":"Returned by Store.ExportTrial when outDir is the empty string. ExportTrial writes trial.json and spans.jsonl into the directory, and it refuses to run without an explicit destination directory (it would otherwise scatter files into the process CWD or fail confusingly).","triggerScenarios":"Calling ExportTrial(plan, \"\") — e.g. the export directory flag was optional, unset, or a preceding directory-selection step failed silently and passed its empty result.","commonSituations":"Shell/CLI wiring where the dir argument is optional with no default; a GUI/script flow in which the user cancelled the folder picker and the code continued with \"\"; refactors that dropped the argument.","solutions":["Require the export-dir flag at the CLI layer and fail with a usage message before calling ExportTrial","Default it to a deterministic location (e.g. ~/.caveman/exports/<trial-id>) when empty is legitimate","Ensure any dir-derivation helper propagates its error instead of returning \"\""],"exampleFix":"// before\nfiles, err := st.ExportTrial(plan, exportDir) // exportDir == \"\"\n\n// after\nif exportDir == \"\" {\n    exportDir = filepath.Join(home, \"exports\", plan.ID)\n}\nfiles, err := st.ExportTrial(plan, exportDir)","handlingStrategy":"validation","validationCode":"if strings.TrimSpace(outDir) == \"\" {\n    outDir = filepath.Join(home, \"exports\", plan.ID) // or reject at CLI\n}","typeGuard":"func hasExportDir(p string) bool { return strings.TrimSpace(p) != \"\" }","tryCatchPattern":"files, err := st.ExportTrial(plan, outDir)\nif err != nil && strings.Contains(err.Error(), \"export directory is required\") {\n    return usageError(\"--dir is required for trial export\")\n}","preventionTips":["Require the export-dir flag or give it a documented default under home","Abort the export flow when a folder-picking step is cancelled instead of continuing with \"\"","Check the directory is on a writable local filesystem before export"],"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"}