{"record":{"id":"497984e9f52f9e8c","repo":"gastownhall/beads","slug":"cannot-specify-both-reason-file-and-reason-r","errorCode":null,"errorMessage":"cannot specify both --reason-file and --reason/--resolution/--message/--comment","messagePattern":"cannot specify both --reason-file and --reason/--resolution/--message/--comment","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/close.go","lineNumber":676,"sourceCode":"\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n// resolveReasonFile resolves the --reason-file flag for `bd close`.\n// Returns (content, true, nil) when --reason-file was set and read successfully.\n// Returns (_, false, nil) when --reason-file was not set.\n// Returns an error on conflict with an existing reason, file read failure, or empty content.\n// Mirrors the --body-file pattern from `bd create` so agents can pass structured close\n// templates without shell-escaping hell.\nfunc resolveReasonFile(cmd *cobra.Command, hasExistingReason bool) (string, bool, error) {\n\tif !cmd.Flags().Changed(\"reason-file\") {\n\t\treturn \"\", false, nil\n\t}\n\tif hasExistingReason {\n\t\treturn \"\", false, fmt.Errorf(\"cannot specify both --reason-file and --reason/--resolution/--message/--comment\")\n\t}\n\tpath, _ := cmd.Flags().GetString(\"reason-file\")\n\tcontent, err := readBodyFile(path)\n\tif err != nil {\n\t\treturn \"\", false, fmt.Errorf(\"reading reason file %q: %w\", path, err)\n\t}\n\tif strings.TrimSpace(content) == \"\" {\n\t\treturn \"\", false, fmt.Errorf(\"--reason-file %q is empty; close reason is required\", path)\n\t}\n\treturn content, true, nil\n}\n\n// resolveCloseTargets resolves a batch of partial issue IDs for `bd close`,\n// preserving input order. For each ID it tries the local store first, then\n// explicit prefix routing via routes.jsonl, then a shared contributor-routed\n// store. This matches resolveAndGetIssueWithRouting's routing precedence.\n//\n// The contributor-routed handle is shared across the batch so bulk close does","sourceCodeStart":658,"sourceCodeEnd":694,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/close.go#L658-L694","documentation":"resolveReasonFile refuses mutually exclusive input: if the --reason-file flag is set while a reason was already provided via --reason/--resolution/--message/--comment, close aborts. This prevents ambiguous close-reason sources.","triggerScenarios":"Invoking `bd close` with both --reason-file <path> and any of --reason, --resolution, --message, or --comment flags.","commonSituations":"Shell scripts adding --reason-file to an existing command line that already passes --reason; copy-pasted command templates merging two reason styles.","solutions":["Remove either the --reason-file flag or the conflicting --reason/--resolution/--message/--comment flag.","Keep a single source of truth for the close reason in your scripts."],"exampleFix":"// before\nbd close bd-1 --reason \"done\" --reason-file note.txt\n// after\nbd close bd-1 --reason-file note.txt","handlingStrategy":"validation","validationCode":"flags=()\n[ -n \"$REASON_FILE\" ] && flags+=(--reason-file \"$REASON_FILE\")\n[ -n \"$REASON\" ] && flags+=(--reason \"$REASON\")\n[ ${#flags[@]} -gt 1 ] && echo 'use only one reason source' && exit 1","typeGuard":"func reasonSourcesConflict(reasonFile string, hasReason bool) bool {\n\treturn reasonFile != \"\" && hasReason\n}","tryCatchPattern":"if _, _, err := resolveReasonFile(cmd, hasReason); err != nil {\n\tif strings.Contains(err.Error(), \"cannot specify both\") {\n\t\t// drop one flag and retry\n\t}\n}","preventionTips":["Standardize on one reason mechanism per script","Build bd close invocations from a single flag set","Lint CI scripts for mutually exclusive bd flags"],"tags":["cli","flags","validation"],"backgroundTag":"conflicting-flags","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}