kovidgoyal/kitty · error

Failed to open controlling terminal with error: %w

Error message

Failed to open controlling terminal with error: %w

What it means

The SSH kitten opens the controlling terminal (with echo disabled) to run interactively; tty.OpenControllingTerm failed, wrapping the OS error. Usually means stdin/stdout is not attached to a TTY or the terminal device can't be reopened.

Source

Thrown at kittens/ssh/main.go:758

		cmcmd = append(cmcmd, "-R", "0:"+listen_on, "-O", "forward")
		cmcmd = append(cmcmd, "--", hostname)
		c := exec.Command(cmcmd[0], cmcmd[1:]...)
		b := bytes.Buffer{}
		c.Stdout = &b
		c.Stderr = os.Stderr
		if err := c.Run(); err != nil {
			return 1, fmt.Errorf("%s\nSetup of port forward in SSH ControlMaster failed with error: %w", b.String(), err)
		}
		port, err := strconv.Atoi(strings.TrimSpace(b.String()))
		if err != nil {
			os.Stderr.Write(b.Bytes())
			return 1, fmt.Errorf("Setup of port forward in SSH ControlMaster failed with error: invalid resolved port returned: %s", b.String())
		}
		cd.listen_on = "tcp:localhost:" + strconv.Itoa(port)
	}
	term, err := tty.OpenControllingTerm(tty.SetNoEcho)
	if err != nil {
		return 1, fmt.Errorf("Failed to open controlling terminal with error: %w", err)
	}
	cd.echo_on = term.WasEchoOnOriginally()
	cd.host_opts, cd.literal_env = host_opts, literal_env
	cd.request_data = need_to_request_data
	cd.hostname_for_match, cd.username = hostname_for_match, uname
	escape_codes_to_set_colors, err := change_colors(cd.host_opts.Color_scheme)
	if err == nil {
		err = term.WriteAllString(escape_codes_to_set_colors + loop.SAVE_PRIVATE_MODE_VALUES + loop.PUSH_KEY_FLAGS + loop.HANDLE_TERMIOS_SIGNALS.EscapeCodeToSet())
	}
	if err != nil {
		return 1, err
	}
	restore_escape_codes := loop.RESTORE_PRIVATE_MODE_VALUES + loop.POP_KEY_FLAGS + loop.HANDLE_TERMIOS_SIGNALS.EscapeCodeToReset()
	if escape_codes_to_set_colors != "" {
		restore_escape_codes += "\x1b[#Q"
	}
	sigs := make(chan os.Signal, 8)
	signal.Notify(sigs, unix.SIGINT, unix.SIGTERM)

View on GitHub (pinned to 6d5d0c4406)

Solutions

  1. Run the ssh kitten inside an interactive kitty window/tab with a real TTY
  2. If in a container, allocate a TTY (docker run -it / exec -it)
  3. Avoid piping stdin/stdout into kitty +kitten ssh; use plain ssh for non-interactive use

Example fix

# before (no tty)
docker exec mycontainer kitty +kitten ssh host

# after
docker exec -it mycontainer kitty +kitten ssh host
Defensive patterns

Strategy: validation

Validate before calling

[ -t 0 ] && [ -t 1 ] || { echo 'needs a TTY'; exit 1; }

Prevention

When it happens

Trigger: Running the ssh kitten where /dev/tty cannot be opened or set to no-echo — piped stdin/stdout, detached process, container without a TTY, or permissions on the terminal device.

Common situations: Running kitty +kitten ssh from a script, CI, docker exec without -t, or an environment where the process has no controlling terminal.

Related errors


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