kovidgoyal/kitty · error

invalid file descriptor: %d

Error message

invalid file descriptor: %d

What it means

When --password fd:N is used, Go's os.NewFile is called with the given integer. If the runtime cannot turn that number into a usable *os.File (it returns nil), the kitten reports 'invalid file descriptor: N'. In practice this branch is rarely hit because most integers yield a non-nil File; read failures surface as error 303 instead.

Source

Thrown at kittens/clipboard/main.go:42

	return run_set_loop(opts, args)
}

func clipboard_main(cmd *cli.Command, opts *Options, args []string) (rc int, err error) {
	if opts.Password != "" {
		if opts.HumanName == "" {
			return 1, fmt.Errorf("must specify --human-name when using a password")
		}
		ptype, val, found := strings.Cut(opts.Password, ":")
		if !found {
			return 1, fmt.Errorf("invalid password: %#v no password type specified", opts.Password)
		}
		switch ptype {
		case "text":
			opts.Password = val
		case "fd":
			if fd, err := strconv.Atoi(val); err == nil {
				if f := os.NewFile(uintptr(fd), "password-fd"); f == nil {
					return 1, fmt.Errorf("invalid file descriptor: %d", fd)
				} else {
					data, err := io.ReadAll(f)
					f.Close()
					if err != nil {
						return 1, fmt.Errorf("failed to read from file descriptor: %d with error: %w", fd, err)
					}
					opts.Password = strings.TrimRightFunc(string(data), unicode.IsSpace)
				}

			} else {
				return 1, fmt.Errorf("not a valid file descriptor number: %#v", val)
			}
		case "file":
			if data, err := os.ReadFile(val); err == nil {
				opts.Password = strings.TrimRightFunc(string(data), unicode.IsSpace)
			} else {
				return 1, fmt.Errorf("failed to read from file: %#v with error: %w", val, err)
			}

View on GitHub (pinned to 6d5d0c4406)

Solutions

  1. Ensure the file descriptor is actually open in the kitten process — open it in the parent shell: `kitten ... --password fd:3 3< password.txt`
  2. Prefer the file: form (`--password file:/path/to/pw`) which avoids fd bookkeeping entirely
  3. Double-check the fd number matches the one you redirected

Example fix

# before
kitten clipboard get --human-name me --password fd:7   # fd 7 not open
# after
kitten clipboard get --human-name me --password fd:3 3< ~/.clipboard-pw
Defensive patterns

Strategy: validation

Validate before calling

# verify the fd is open and readable before exec'ing kitten
if ! { : <&3; } 2>/dev/null; then echo "fd 3 not readable"; exit 2; fi
kitten clipboard get --human-name me --password fd:3 ...

Prevention

When it happens

Trigger: Passing `--password fd:N` where N is a number that cannot be wrapped into a file handle by os.NewFile at all (nil return), typically a degenerate value on the current platform.

Common situations: Typos in the fd number; platforms/runtime versions where os.NewFile returns nil for out-of-range descriptors; passing an fd that was never opened by the parent process (which usually fails later at read time instead).

Related errors


AI-assisted analysis of kovidgoyal/kitty@6d5d0c4406 (2026-08-27). Data as JSON: /api/errors/2bdbfe71f22adb3e. Report an issue: GitHub.