kovidgoyal/kitty · error

failed to read from file descriptor: %d with error: %w

Error message

failed to read from file descriptor: %d with error: %w

What it means

After successfully wrapping --password fd:N into a *os.File, the kitten reads it to EOF. If the read itself fails (I/O error, closed pipe, bad fd at read time), the error is wrapped with the offending descriptor number and the underlying cause.

Source

Thrown at kittens/clipboard/main.go:47

		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)
			}
		}
	}
	if len(args) > 0 {
		return 0, run_mime_loop(opts, args)
	}

View on GitHub (pinned to 6d5d0c4406)

Solutions

  1. Verify the fd is readable: `ls -l /proc/self/fd/N` in the same process context, or test with `cat <&N`
  2. Keep the writer alive until the kitten has read the password, or use a regular file: `--password file:pw.txt`
  3. Check the underlying error message (%w) to identify the true errno (EBADF, EPIPE, etc.)

Example fix

# before
kitten clipboard get --human-name me --password fd:3  # fd 3 is write-only
# after
kitten clipboard get --human-name me --password file:/etc/myapp/clipboard-pw
Defensive patterns

Strategy: fallback

Validate before calling

# fall back to file: if reading the fd may fail
[ -r "$PWFILE" ] && PWARG="file:$PWFILE" || PWARG="fd:3"

Prevention

When it happens

Trigger: `--password fd:N` where the descriptor exists but reading fails: the write end of a pipe was closed, the fd refers to a write-only or closed handle, or the underlying file hit an I/O error during io.ReadAll.

Common situations: Opening a fd write-only and passing it for reading; the producer process exiting before the kitten reads; passing a socket or device fd that errors on read; fd inheritance issues under systemd or container runtimes that close inherited fds.

Related errors


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