gastownhall/beads · error

failed to open file: %w

Error message

failed to open file: %w

What it means

readBodyFile loads body text for command flags (--description-file, --design-file, --reason-file, etc.) from a file path or '-' for stdin. This error wraps os.Open failures: the file could not be opened at all. It is a straightforward file-path/permission problem surfaced when the CLI tries to read user-supplied body content.

Source

Thrown at cmd/bd/flags.go:198

		v, _ := cmd.Flags().GetString("design")
		return v, true, nil
	}

	return "", false, nil
}

// readBodyFile reads the description content from a file.
// If filePath is "-", reads from stdin.
func readBodyFile(filePath string) (string, error) {
	var reader io.Reader

	if filePath == "-" {
		reader = os.Stdin
	} else {
		// #nosec G304 - filePath comes from user flag, validated by caller
		file, err := os.Open(filePath)
		if err != nil {
			return "", fmt.Errorf("failed to open file: %w", err)
		}
		defer file.Close()
		reader = file
	}

	content, err := io.ReadAll(reader)
	if err != nil {
		return "", fmt.Errorf("failed to read file: %w", err)
	}

	return string(content), nil
}

// textSources names the places a command can take body text from: stdin, a
// file path, an explicit text flag, then positional args. A non-nil stdin
// means --stdin was given. flagName names the text flag (e.g. "--response")
// in conflict errors and always accompanies flagText. flagSet marks the text
// flag as explicitly passed (cobra's Changed), so an empty flag value still

View on GitHub (pinned to 71377f2769)

Solutions

  1. Verify the path exists and is readable: ls -l <path>; correct typos and use an absolute path if unsure
  2. Check that you didn't pass a directory instead of a file
  3. Fix file permissions (chmod/chown) or run from a context/user that can read it
  4. If the text is short or generated, pipe it via stdin with '-' or pass it inline instead of via a file

Example fix

// before: path from an unset variable
bd create "T" --description-file="$NOTES_FILE"   // failed to open file: open : no such file or directory
// after: guard the variable before invoking
[ -n "$NOTES_FILE" ] && [ -f "$NOTES_FILE" ] && bd create "T" --description-file="$NOTES_FILE"
Defensive patterns

Strategy: validation

Validate before calling

path := filePath
if path != "-" {
    info, err := os.Stat(path)
    if err != nil {
        return fmt.Errorf("body file not accessible: %w", err)
    }
    if info.IsDir() {
        return fmt.Errorf("body file is a directory: %s", path)
    }
    f, err := os.Open(path)
    if err != nil { return err }
    f.Close()
}

Type guard

func readableFile(path string) bool {
    if path == "-" { return true }
    f, err := os.Open(path)
    if err != nil { return false }
    f.Close()
    return true
}

Try / catch

content, err := readBodyFile(flagPath)
if err != nil {
    if errors.Is(err, os.ErrNotExist) {
        return fmt.Errorf("--description-file: no such file: %s", flagPath)
    }
    if errors.Is(err, os.ErrPermission) {
        return fmt.Errorf("--description-file: permission denied: %s", flagPath)
    }
    return err
}

Prevention

When it happens

Trigger: Passing --description-file/--design-file/--reason-file with a path that does not exist, is a directory, or lacks read permission; a typo'd path; using '-' when stdin is closed/unavailable (less common; that path bypasses os.Open).

Common situations: Shell variable empty or unset so the flag receives a truncated path; running from a different working directory than assumed; file deleted between tab-completion and execution; permissions tightened by sandboxed CI environments.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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