gastownhall/beads · error
--reason-file %q is empty; close reason is required
Error message
--reason-file %q is empty; close reason is required
What it means
After successfully reading --reason-file, resolveReasonFile rejects content that is empty or whitespace-only, since a close reason is mandatory. This catches silent failures like an empty file or a file containing only newlines.
Source
Thrown at cmd/bd/close.go:684
// 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 does
// not repeatedly open the same planning store and every result has a clear store
// owner for subsequent close-time checks and writes.
//
// Each returned RoutedResult.Store points to whichever store actually owns the
// issue. The caller invokes cleanup() once when done; per-result Close() is a
// no-op for routed-via-shared-handle entries because they don't own the handle.
func resolveCloseTargets(ctx context.Context, localStore storage.DoltStorage, ids []string) ([]*RoutedResult, func(), error) {
results := make([]*RoutedResult, 0, len(ids))View on GitHub (pinned to 71377f2769)
Solutions
- Populate the reason file with non-empty text and retry.
- Check the upstream command that generated the file for silent failures.
- Fall back to an inline --reason for this invocation.
Example fix
// before bd close bd-1 --reason-file empty.txt // error: --reason-file "empty.txt" is empty // after echo "completed in sprint 12" > reason.txt && bd close bd-1 --reason-file reason.txt
Defensive patterns
Strategy: validation
Validate before calling
[ -s "$REASON_FILE" ] || { echo "reason file is empty" >&2; exit 1; }
# -s fails on zero-size; also check whitespace:
grep -q '[^[:space:]]' "$REASON_FILE" || exit 1 Try / catch
if err := runClose(...); err != nil {
if strings.Contains(err.Error(), "is empty; close reason is required") {
// regenerate the reason file or fall back to --reason
}
} Prevention
- Fail fast when generating reason files from pipelines
- Use grep for non-whitespace, not just file size
- Validate upstream command exit codes that produce the file
When it happens
Trigger: --reason-file points to an existing but empty (or whitespace-only) file, including output of a failed pipeline redirected into the file.
Common situations: CI step writing the reason failed silently and produced an empty file; an editor saved a blank note; stdin redirected from an empty stream with `--reason-file -`.
Related errors
- got %d close reasons for %d issue IDs; provide exactly one s
- cannot specify both --reason-file and --reason/--resolution/
- reading reason file %q: %w
- invalid mode '%s', must be 'compile' or 'runtime'
- runtime mode requires all variables to have values Missing:
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/8d6472aea98d20e2.
Report an issue: GitHub.