{"record":{"id":"9c59619c1467755a","repo":"gastownhall/beads","slug":"reading-confirmation-w","errorCode":null,"errorMessage":"reading confirmation: %w","messagePattern":"reading confirmation: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/migrate_hooks_apply.go","lineNumber":445,"sourceCode":"\nfunc filesEqual(pathA, pathB string) (bool, error) {\n\ta, err := os.ReadFile(pathA) // #nosec G304 -- compared paths come from deterministic migration operations\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tb, err := os.ReadFile(pathB) // #nosec G304 -- compared paths come from deterministic migration operations\n\tif err != nil {\n\t\treturn false, err\n\t}\n\treturn bytes.Equal(a, b), nil\n}\n\nfunc confirmHookMigrationApply(totalOperations int) (bool, error) {\n\tfmt.Printf(\"\\nThis will apply %d hook migration operation(s). Continue? (Y/n): \", totalOperations)\n\treader := bufio.NewReader(os.Stdin)\n\tresponse, err := reader.ReadString('\\n')\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"reading confirmation: %w\", err)\n\t}\n\tresponse = strings.TrimSpace(strings.ToLower(response))\n\tif response == \"\" || response == \"y\" || response == \"yes\" {\n\t\treturn true, nil\n\t}\n\treturn false, nil\n}\n","sourceCodeStart":427,"sourceCodeEnd":453,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/migrate_hooks_apply.go#L427-L453","documentation":"This wraps a failure reading the y/n confirmation answer from stdin during `bd migrate`'s interactive apply prompt. ReadString('\\n') fails if stdin is closed (EOF) or is not a readable stream. The migration aborts without applying anything.","triggerScenarios":"Running `bd migrate` in a non-interactive context: stdin is /dev/null (CI, cron), a pipe that closed early, or stdin redirected from an empty/exhausted file, so ReadString returns io.EOF or another read error.","commonSituations":"CI pipelines and scripts that invoke bd without a TTY; `bd migrate < /dev/null`; piped invocation like `echo | bd migrate` where the pipe closes before input; nested tooling (agents) running bd headlessly.","solutions":["Pipe the confirmation explicitly: `echo y | bd migrate` (empty line also confirms, so `< /dev/null` errors but `printf '\\n' |` succeeds only if it returns cleanly).","Run the command interactively in a terminal where stdin is open.","Use a non-interactive flag if the migration supports one to skip the prompt entirely.","In CI, gate the migration behind an explicit approval step that pipes the answer."],"exampleFix":"// before\n$ bd migrate < /dev/null\nreading confirmation: EOF\n// after\n$ echo y | bd migrate","handlingStrategy":"try-catch","validationCode":"stat, err := os.Stdin.Stat()\nif err != nil || (stat.Mode()&os.ModeCharDevice) == 0 {\n\t// stdin is not a TTY: provide input via pipe or use non-interactive mode\n}","typeGuard":null,"tryCatchPattern":"if err != nil {\n\tif errors.Is(err, io.EOF) || errors.Is(err, os.ErrClosed) {\n\t\t// stdin closed: abort or default to confirm in non-interactive mode\n\t\treturn false, nil\n\t}\n\treturn false, fmt.Errorf(\"reading confirmation: %w\", err)\n}","preventionTips":["Always pipe the answer when invoking bd from scripts/CI: echo y | bd migrate","Detect non-TTY stdin and skip interactive prompts (use flags instead)","Don't redirect stdin from empty files or closed pipes"],"tags":["stdin","interactive","migration","cli"],"backgroundTag":"stdin-eof","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}