antlr/antlr4 · error

output directory `%s` is not a directory

Error message

output directory `%s` is not a directory

What it means

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.

Source

Thrown at runtime/Go/antlr/v4/statistics.go:139

	sort.Slice(s.jStats, func(i, j int) bool {
		return s.jStats[i].Gets+s.jStats[i].Puts > s.jStats[j].Gets+s.jStats[j].Puts
	})
	for i := 0; i < len(s.jStats) && i < s.topN; i++ {
		s.topNByUsed = append(s.topNByUsed, s.jStats[i])
	}
}

// Report dumps a markdown formatted report of all the statistics collected during a run to the given dir output
// path, which should represent a directory. Generated files will be prefixed with the given prefix and will be
// given a type name such as `anomalies` and a time stamp such as `2021-09-01T12:34:56` and a .md suffix.
func (s *goRunStats) Report(dir string, prefix string) error {

	isDir, err := isDirectory(dir)
	switch {
	case err != nil:
		return err
	case !isDir:
		return fmt.Errorf("output directory `%s` is not a directory", dir)
	}
	s.reportCollections(dir, prefix)

	// Clean out any old data in case the user forgets
	//
	s.Reset()
	return nil
}

func (s *goRunStats) Reset() {
	s.jStats = nil
	s.topNByUsed = nil
	s.topNByMax = nil
}

func (s *goRunStats) reportCollections(dir, prefix string) {
	cname := filepath.Join(dir, ".asciidoctor")
	// If the file doesn't exist, create it, or append to the file

View on GitHub (pinned to 7d5770395b)

Solutions

  1. Point Report() at an existing directory
  2. Create the directory first with os.MkdirAll(dir, 0755) before calling Report
  3. Check the returned error and log it; note Report also resets stats, so only re-run after fixing the path

Example fix

// before
err := stats.Report("/tmp/report.md", "run")

// after
err := os.MkdirAll("/tmp/reports", 0o755)
if err != nil { return err }
err = stats.Report("/tmp/reports", "run")
Defensive patterns

Strategy: validation

Validate before calling

info, err := os.Stat(dir)
if err != nil || !info.IsDir() {
    if err := os.MkdirAll(dir, 0o755); err != nil {
        return err
    }
}
err = stats.Report(dir, prefix)

Type guard

func isDir(path string) bool {
    info, err := os.Stat(path)
    return err == nil && info.IsDir()
}

Try / catch

if err := stats.Report(dir, prefix); err != nil {
    if strings.Contains(err.Error(), "is not a directory") {
        // fix path and mkdir, then retry once
    }
}

Prevention

When it happens

Trigger: Calling stats.Report('/some/path/file.txt', 'prefix') or any path occupied by a symlink/file, or when isDirectory fails on a broken path.

Common situations: 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.

Related errors


AI-assisted analysis of antlr/antlr4@7d5770395b (2026-08-14). Data as JSON: /api/errors/0bd7edf5630b346c. Report an issue: GitHub.