{"record":{"id":"0bd7edf5630b346c","repo":"antlr/antlr4","slug":"output-directory-s-is-not-a-directory","errorCode":null,"errorMessage":"output directory `%s` is not a directory","messagePattern":"output directory `(.+?)` is not a directory","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"runtime/Go/antlr/v4/statistics.go","lineNumber":139,"sourceCode":"\tsort.Slice(s.jStats, func(i, j int) bool {\n\t\treturn s.jStats[i].Gets+s.jStats[i].Puts > s.jStats[j].Gets+s.jStats[j].Puts\n\t})\n\tfor i := 0; i < len(s.jStats) && i < s.topN; i++ {\n\t\ts.topNByUsed = append(s.topNByUsed, s.jStats[i])\n\t}\n}\n\n// Report dumps a markdown formatted report of all the statistics collected during a run to the given dir output\n// path, which should represent a directory. Generated files will be prefixed with the given prefix and will be\n// given a type name such as `anomalies` and a time stamp such as `2021-09-01T12:34:56` and a .md suffix.\nfunc (s *goRunStats) Report(dir string, prefix string) error {\n\n\tisDir, err := isDirectory(dir)\n\tswitch {\n\tcase err != nil:\n\t\treturn err\n\tcase !isDir:\n\t\treturn fmt.Errorf(\"output directory `%s` is not a directory\", dir)\n\t}\n\ts.reportCollections(dir, prefix)\n\n\t// Clean out any old data in case the user forgets\n\t//\n\ts.Reset()\n\treturn nil\n}\n\nfunc (s *goRunStats) Reset() {\n\ts.jStats = nil\n\ts.topNByUsed = nil\n\ts.topNByMax = nil\n}\n\nfunc (s *goRunStats) reportCollections(dir, prefix string) {\n\tcname := filepath.Join(dir, \".asciidoctor\")\n\t// If the file doesn't exist, create it, or append to the file","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/antlr/antlr4/blob/7d5770395bb7b02eb56e7c62662cb1d7c08f42a3/runtime/Go/antlr/v4/statistics.go#L121-L157","documentation":"Returned as an error by goRunStats.Report() in the Go runtime when the dir argument exists but is not a directory (isDirectory reports false). Report writes markdown statistics files into that directory, so a regular file at the path is unusable.","triggerScenarios":"Calling stats.Report('/some/path/file.txt', 'prefix') or any path occupied by a symlink/file, or when isDirectory fails on a broken path.","commonSituations":"Configured statistics output path pointing at a file, a missing parent directory combined with a stale file, or running with a path from a different environment.","solutions":["Point Report() at an existing directory","Create the directory first with os.MkdirAll(dir, 0755) before calling Report","Check the returned error and log it; note Report also resets stats, so only re-run after fixing the path"],"exampleFix":"// before\nerr := stats.Report(\"/tmp/report.md\", \"run\")\n\n// after\nerr := os.MkdirAll(\"/tmp/reports\", 0o755)\nif err != nil { return err }\nerr = stats.Report(\"/tmp/reports\", \"run\")","handlingStrategy":"validation","validationCode":"info, err := os.Stat(dir)\nif err != nil || !info.IsDir() {\n    if err := os.MkdirAll(dir, 0o755); err != nil {\n        return err\n    }\n}\nerr = stats.Report(dir, prefix)","typeGuard":"func isDir(path string) bool {\n    info, err := os.Stat(path)\n    return err == nil && info.IsDir()\n}","tryCatchPattern":"if err := stats.Report(dir, prefix); err != nil {\n    if strings.Contains(err.Error(), \"is not a directory\") {\n        // fix path and mkdir, then retry once\n    }\n}","preventionTips":["Create the output directory with MkdirAll before calling Report","Always check Report's returned error; Report also resets stats on success"],"tags":["antlr","go","statistics","filesystem"],"backgroundTag":null,"analyzedSha":"7d5770395bb7b02eb56e7c62662cb1d7c08f42a3","analyzedAt":"2026-08-14T14:47:56.354Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}