{"record":{"id":"41c1df57338b984d","repo":"gastownhall/beads","slug":"reading-reason-file-q-w","errorCode":null,"errorMessage":"reading reason file %q: %w","messagePattern":"reading reason file %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/close.go","lineNumber":681,"sourceCode":"}\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\n// not repeatedly open the same planning store and every result has a clear store\n// owner for subsequent close-time checks and writes.\n//\n// Each returned RoutedResult.Store points to whichever store actually owns the\n// issue. The caller invokes cleanup() once when done; per-result Close() is a","sourceCodeStart":663,"sourceCodeEnd":699,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/close.go#L663-L699","documentation":"When --reason-file is given, resolveReasonFile reads it via readBodyFile and wraps any read failure with the offending path (`reading reason file %q: %w`). This surfaces filesystem-level problems (missing file, permission denied, read error) in the context of the close command.","triggerScenarios":"--reason-file points to a nonexistent file, an unreadable path, or a directory; readBodyFile returns any I/O error.","commonSituations":"Typo in the file path; running from a different working directory with a relative path; file deleted between scripting steps; permission restrictions in CI.","solutions":["Verify the path exists and is readable (`cat <path>` or `test -r <path>`).","Use an absolute path or correct the relative path.","If '-' is intended to read stdin, pass --reason-file - explicitly."],"exampleFix":"// before\nbd close bd-1 --reason-file ./notes.txt  # file absent\n// after\nbd close bd-1 --reason-file /abs/path/notes.txt  # ensure file exists first","handlingStrategy":"validation","validationCode":"test -r \"$REASON_FILE\" || { echo \"reason file missing: $REASON_FILE\" >&2; exit 1; }","typeGuard":null,"tryCatchPattern":"if err := runClose(...); err != nil {\n\tvar wrapped string\n\tif strings.Contains(err.Error(), \"reading reason file\") {\n\t\t// check path existence/permissions, then retry\n\t}\n\t_ = wrapped\n}","preventionTips":["Use absolute paths for reason files in scripts","Create the reason file before invoking bd close","Add existence checks in CI before close steps"],"tags":["cli","filesystem","reason-file"],"backgroundTag":"file-not-found","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}