gastownhall/beads · error

reading confirmation: %w

Error message

reading confirmation: %w

What it means

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.

Source

Thrown at cmd/bd/migrate_hooks_apply.go:445

func filesEqual(pathA, pathB string) (bool, error) {
	a, err := os.ReadFile(pathA) // #nosec G304 -- compared paths come from deterministic migration operations
	if err != nil {
		return false, err
	}
	b, err := os.ReadFile(pathB) // #nosec G304 -- compared paths come from deterministic migration operations
	if err != nil {
		return false, err
	}
	return bytes.Equal(a, b), nil
}

func confirmHookMigrationApply(totalOperations int) (bool, error) {
	fmt.Printf("\nThis will apply %d hook migration operation(s). Continue? (Y/n): ", totalOperations)
	reader := bufio.NewReader(os.Stdin)
	response, err := reader.ReadString('\n')
	if err != nil {
		return false, fmt.Errorf("reading confirmation: %w", err)
	}
	response = strings.TrimSpace(strings.ToLower(response))
	if response == "" || response == "y" || response == "yes" {
		return true, nil
	}
	return false, nil
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. 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).
  2. Run the command interactively in a terminal where stdin is open.
  3. Use a non-interactive flag if the migration supports one to skip the prompt entirely.
  4. In CI, gate the migration behind an explicit approval step that pipes the answer.

Example fix

// before
$ bd migrate < /dev/null
reading confirmation: EOF
// after
$ echo y | bd migrate
Defensive patterns

Strategy: try-catch

Validate before calling

stat, err := os.Stdin.Stat()
if err != nil || (stat.Mode()&os.ModeCharDevice) == 0 {
	// stdin is not a TTY: provide input via pipe or use non-interactive mode
}

Try / catch

if err != nil {
	if errors.Is(err, io.EOF) || errors.Is(err, os.ErrClosed) {
		// stdin closed: abort or default to confirm in non-interactive mode
		return false, nil
	}
	return false, fmt.Errorf("reading confirmation: %w", err)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/9c59619c1467755a. Report an issue: GitHub.