{"record":{"id":"1e9653c68771707a","repo":"alibaba/open-code-review","slug":"output-q-is-a-directory","errorCode":null,"errorMessage":"--output %q is a directory","messagePattern":"--output %q is a directory","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/opencodereview/shared.go","lineNumber":580,"sourceCode":"\treturn w.file.Close()\n}\n\n// resolveOutputWriter resolves the --output target into a writer plus a\n// cleanup function.\n//   - \"\" or \"-\"      → os.Stdout with a no-op cleanup (colors preserved, no hint)\n//   - otherwise      → a lazyFileWriter over os.Create(path), deferred until the\n//     first Write; text format wraps the file in stripAnsiWriter so ANSI\n//     colors never reach the result file.\n//\n// Fail-fast checks (directory target, missing parent) run here without\n// creating or truncating anything; deeper errors (permissions, disk) surface\n// on the first Write and fail the command non-zero.\nfunc resolveOutputWriter(path, format string) (io.Writer, func() error, error) {\n\tif path == \"\" || path == \"-\" {\n\t\treturn os.Stdout, func() error { return nil }, nil\n\t}\n\tif st, err := os.Stat(path); err == nil && st.IsDir() {\n\t\treturn nil, nil, fmt.Errorf(\"--output %q is a directory\", path)\n\t}\n\tparent := filepath.Dir(path)\n\tif st, err := os.Stat(parent); err != nil || !st.IsDir() {\n\t\treturn nil, nil, fmt.Errorf(\"--output directory does not exist: %s\", parent)\n\t}\n\tw := &lazyFileWriter{path: path, strip: !isMachineReadable(format)}\n\treturn w, w.Close, nil\n}\n\n// ResultProvider abstracts the metadata both internal/agent.Agent and\n// internal/scan.Agent expose post-run, so emitRunResult can finalize\n// either without knowing which kind it has.\ntype ResultProvider interface {\n\tDiffs() []model.Diff\n\tFilesReviewed() int64\n\tTotalInputTokens() int64\n\tTotalOutputTokens() int64\n\tTotalTokensUsed() int64","sourceCodeStart":562,"sourceCodeEnd":598,"githubUrl":"https://github.com/alibaba/open-code-review/blob/5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f/cmd/opencodereview/shared.go#L562-L598","documentation":"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.","triggerScenarios":"ocr review/scan run with --output set to a directory path such as `--output ./reports` or `--output .` instead of a file path.","commonSituations":"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.","solutions":["Pass a full file path, e.g. --output ./reports/review.md","If you meant stdout, use --output - or omit --output","Remove or rename the directory if it was created by mistake"],"exampleFix":"// before\nocr review --output ./reports\n// after\nocr review --output ./reports/review.md","handlingStrategy":"validation","validationCode":"if out != \"\" && out != \"-\" {\n\tif st, err := os.Stat(out); err == nil && st.IsDir() {\n\t\treturn fmt.Errorf(\"--output must be a file path, got directory %s\", out)\n\t}\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pass a filename, not a directory, to --output","Use --output - for stdout when a file target is ambiguous","In scripts, build output paths with filepath.Join(dir, \"review.md\") rather than passing dir directly"],"tags":["go","cli","path-validation"],"backgroundTag":"output-is-a-directory","analyzedSha":"5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f","analyzedAt":"2026-09-02T02:08:09.116Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}