alibaba/open-code-review · error

--output %q is a directory

Error message

--output %q is a directory

What it means

resolveOutputWriter validates the --output path up front and rejects a path that currently exists as a directory with `--output %q is a directory`. This pre-flight check prevents lazyFileWriter from failing later (os.Create would return EISDIR) so the user gets a clear message immediately.

Source

Thrown at cmd/opencodereview/shared.go:580

	return w.file.Close()
}

// resolveOutputWriter resolves the --output target into a writer plus a
// cleanup function.
//   - "" or "-"      → os.Stdout with a no-op cleanup (colors preserved, no hint)
//   - otherwise      → a lazyFileWriter over os.Create(path), deferred until the
//     first Write; text format wraps the file in stripAnsiWriter so ANSI
//     colors never reach the result file.
//
// Fail-fast checks (directory target, missing parent) run here without
// creating or truncating anything; deeper errors (permissions, disk) surface
// on the first Write and fail the command non-zero.
func resolveOutputWriter(path, format string) (io.Writer, func() error, error) {
	if path == "" || path == "-" {
		return os.Stdout, func() error { return nil }, nil
	}
	if st, err := os.Stat(path); err == nil && st.IsDir() {
		return nil, nil, fmt.Errorf("--output %q is a directory", path)
	}
	parent := filepath.Dir(path)
	if st, err := os.Stat(parent); err != nil || !st.IsDir() {
		return nil, nil, fmt.Errorf("--output directory does not exist: %s", parent)
	}
	w := &lazyFileWriter{path: path, strip: !isMachineReadable(format)}
	return w, w.Close, nil
}

// ResultProvider abstracts the metadata both internal/agent.Agent and
// internal/scan.Agent expose post-run, so emitRunResult can finalize
// either without knowing which kind it has.
type ResultProvider interface {
	Diffs() []model.Diff
	FilesReviewed() int64
	TotalInputTokens() int64
	TotalOutputTokens() int64
	TotalTokensUsed() int64

View on GitHub (pinned to 5cf97d0d15)

Solutions

  1. Pass a full file path, e.g. --output ./reports/review.md
  2. If you meant stdout, use --output - or omit --output
  3. Remove or rename the directory if it was created by mistake

Example fix

// before
ocr review --output ./reports
// after
ocr review --output ./reports/review.md
Defensive patterns

Strategy: validation

Validate before calling

if out != "" && out != "-" {
	if st, err := os.Stat(out); err == nil && st.IsDir() {
		return fmt.Errorf("--output must be a file path, got directory %s", out)
	}
}

Prevention

When it happens

Trigger: ocr review/scan run with --output set to a directory path such as `--output ./reports` or `--output .` instead of a file path.

Common situations: Shell completion or copy-paste yielding a directory; intending 'write into this directory' but passing the directory itself; a stale script where the target name became a directory in a later step.

Related errors


AI-assisted analysis of alibaba/open-code-review@5cf97d0d15 (2026-09-02). Data as JSON: /api/errors/1e9653c68771707a. Report an issue: GitHub.