gastownhall/beads · error
cannot specify both --reason-file and --reason/--resolution/
Error message
cannot specify both --reason-file and --reason/--resolution/--message/--comment
What it means
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.
Source
Thrown at cmd/bd/close.go:676
return true
}
}
return false
}
// resolveReasonFile resolves the --reason-file flag for `bd close`.
// Returns (content, true, nil) when --reason-file was set and read successfully.
// Returns (_, false, nil) when --reason-file was not set.
// Returns an error on conflict with an existing reason, file read failure, or empty content.
// Mirrors the --body-file pattern from `bd create` so agents can pass structured close
// templates without shell-escaping hell.
func resolveReasonFile(cmd *cobra.Command, hasExistingReason bool) (string, bool, error) {
if !cmd.Flags().Changed("reason-file") {
return "", false, nil
}
if hasExistingReason {
return "", false, fmt.Errorf("cannot specify both --reason-file and --reason/--resolution/--message/--comment")
}
path, _ := cmd.Flags().GetString("reason-file")
content, err := readBodyFile(path)
if err != nil {
return "", false, fmt.Errorf("reading reason file %q: %w", path, err)
}
if strings.TrimSpace(content) == "" {
return "", false, fmt.Errorf("--reason-file %q is empty; close reason is required", path)
}
return content, true, nil
}
// resolveCloseTargets resolves a batch of partial issue IDs for `bd close`,
// preserving input order. For each ID it tries the local store first, then
// explicit prefix routing via routes.jsonl, then a shared contributor-routed
// store. This matches resolveAndGetIssueWithRouting's routing precedence.
//
// The contributor-routed handle is shared across the batch so bulk close doesView on GitHub (pinned to 71377f2769)
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.
Example fix
// before bd close bd-1 --reason "done" --reason-file note.txt // after bd close bd-1 --reason-file note.txt
Defensive patterns
Strategy: validation
Validate before calling
flags=()
[ -n "$REASON_FILE" ] && flags+=(--reason-file "$REASON_FILE")
[ -n "$REASON" ] && flags+=(--reason "$REASON")
[ ${#flags[@]} -gt 1 ] && echo 'use only one reason source' && exit 1 Type guard
func reasonSourcesConflict(reasonFile string, hasReason bool) bool {
return reasonFile != "" && hasReason
} Try / catch
if _, _, err := resolveReasonFile(cmd, hasReason); err != nil {
if strings.Contains(err.Error(), "cannot specify both") {
// drop one flag and retry
}
} Prevention
- Standardize on one reason mechanism per script
- Build bd close invocations from a single flag set
- Lint CI scripts for mutually exclusive bd flags
When it happens
Trigger: Invoking `bd close` with both --reason-file <path> and any of --reason, --resolution, --message, or --comment flags.
Common situations: Shell scripts adding --reason-file to an existing command line that already passes --reason; copy-pasted command templates merging two reason styles.
Related errors
- invalid --dolt-auto-commit=%q (valid: off, on, batch)
- --server-host cannot be empty; omit the flag to use BEADS_DO
- --server-user cannot be empty; omit the flag to use BEADS_DO
- cannot use both --pull-only and --push-only
- %w (--prefer-local, --prefer-ado, --prefer-newer)
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/497984e9f52f9e8c.
Report an issue: GitHub.