kovidgoyal/kitty · error · Exception

The remote control password was invalid: {data!r}

Error message

The remote control password was invalid: {data!r}

What it means

Raised during handler initialization when remote control password negotiation fails: the kitten reads up to 256 bytes from the inherited remote-control socket and expects the response to end with a newline. If the data does not end in b'\n' it means the peer did not send a valid password line (wrong protocol, closed socket, or garbage), so the password cannot be extracted.

Source

Thrown at kittens/tui/handler.py:84

    def initialize(self) -> None:
        if self.initialized:
            return
        self.initialized = True
        if running_in_kitty():
            return
        if self.allow_remote_control:
            self.to = os.environ.get('KITTY_LISTEN_ON', '')
            if not self.to:
                raise ValueError('Remote control not enabled, this kitten should be run via a map in kitty.conf, not from the command line')
            self.rc_fd = int(self.to.partition(':')[-1])
            os.set_inheritable(self.rc_fd, False)
        if (self.remote_control_password or self.remote_control_password == '') and not self.password:
            import socket

            with socket.fromfd(self.rc_fd, socket.AF_UNIX, socket.SOCK_STREAM) as s:
                data = s.recv(256)
            if not data.endswith(b'\n'):
                raise Exception(f'The remote control password was invalid: {data!r}')
            self.password = data.strip().decode()

    def __call__(self, args: list[str]) -> str:
        self.initialize()
        return self.func(args)

    def allow_indiscriminate_remote_control(self, enable: bool = True) -> None:
        if self.rc_fd > -1:
            if enable:
                os.set_inheritable(self.rc_fd, True)
                if self.password:
                    os.environ['KITTY_RC_PASSWORD'] = self.password
            else:
                os.set_inheritable(self.rc_fd, False)
                if self.password:
                    os.environ.pop('KITTY_RC_PASSWORD', None)

    def remote_control(self, cmd: str | Sequence[str], **kw: Any) -> Any:

View on GitHub (pinned to 6d5d0c4406)

Solutions

  1. Ensure the kitten is launched by the same kitty version that provides the socket (update kitty entirely rather than mixing binaries)
  2. Launch via a kitty.conf map so FD inheritance is guaranteed
  3. Pass the password explicitly (set self.password) to skip socket negotiation

Example fix

# before
handler = ResultHandler(func, allow_remote_control=True, remote_control_password='')  # negotiates over socket
# after
handler.password = os.environ['KITTY_RC_PASSWORD']  # supply directly, skip negotiation
Defensive patterns

Strategy: try-catch

Try / catch

try:
    handler.initialize()
except Exception as e:
    if 'remote control password was invalid' in str(e):
        handler.password = os.environ.get('KITTY_RC_PASSWORD', '')
    else:
        raise

Prevention

When it happens

Trigger: Calling a TUI handler with remote_control_password set (or empty string) and no explicit password, where the socket behind KITTY_LISTEN_ON does not deliver a newline-terminated password line — e.g. the FD points to the wrong socket, an incompatible kitty version, or the socket was already consumed/closed.

Common situations: Mismatched kitty versions between the running terminal and the kitten code; KITTY_LISTEN_ON pointing at another process's socket; the rc_fd inheritance broken by an intermediate wrapper (shell, sandbox, systemd unit) that closed or duplicated FDs.

Related errors


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