yudai/gotty · error

failed to listen at `%s`

Error message

failed to listen at `%s`

What it means

Run() calls net.Listen("tcp", address:port) to bind the listener. If the OS refuses the bind (port in use, permission denied, invalid address), the error is wrapped with the host:port that failed. GoTTY exits and no server is started.

Source

Thrown at server/server.go:122

	srv, err := server.setupHTTPServer(handlers)
	if err != nil {
		return errors.Wrapf(err, "failed to setup an HTTP server")
	}

	if server.options.PermitWrite {
		log.Printf("Permitting clients to write input to the PTY.")
	}
	if server.options.Once {
		log.Printf("Once option is provided, accepting only one client")
	}

	if server.options.Port == "0" {
		log.Printf("Port number configured to `0`, choosing a random port")
	}
	hostPort := net.JoinHostPort(server.options.Address, server.options.Port)
	listener, err := net.Listen("tcp", hostPort)
	if err != nil {
		return errors.Wrapf(err, "failed to listen at `%s`", hostPort)
	}

	scheme := "http"
	if server.options.EnableTLS {
		scheme = "https"
	}
	host, port, _ := net.SplitHostPort(listener.Addr().String())
	log.Printf("HTTP server is listening at: %s", scheme+"://"+host+":"+port+path)
	if server.options.Address == "0.0.0.0" {
		for _, address := range listAddresses() {
			log.Printf("Alternative URL: %s", scheme+"://"+address+":"+port+path)
		}
	}

	srvErr := make(chan error, 1)
	go func() {
		if server.options.EnableTLS {
			crtFile := homedir.Expand(server.options.TLSCrtFile)

View on GitHub (pinned to a080c85cbc)

Solutions

  1. Choose a free port (or port "0" to let GoTTY pick a random one)
  2. Stop the process occupying the port (lsof -i :PORT / netstat)
  3. Use address 0.0.0.0 or 127.0.0.1 instead of an unassigned interface address
  4. For privileged ports, run with root/cap_net_bind_service or use a high port

Example fix

// before
port = "80"
// after
port = "8080"
Defensive patterns

Strategy: retry

Validate before calling

func portFree(addr, port string) bool {
    ln, err := net.Listen("tcp", net.JoinHostPort(addr, port))
    if err != nil { return false }
    ln.Close()
    return true
}

Try / catch

if err := srv.Run(ctx); err != nil {
    var nerr *net.OpError
    if errors.As(err, &nerr) {
        // address already in use / permission denied — pick another port
    }
}

Prevention

When it happens

Trigger: Calling Run() when options.Port is already bound by another process, below 1024 without privileges, or options.Address is not a local IP/hostname.

Common situations: Another GoTTY or web server already on port 8080; running in a container without the port mapped/allowed; address typo like 0.0.0.0 vs an unassigned interface IP; port 80/443 without root (or setcap).


AI-assisted analysis of yudai/gotty@a080c85cbc (2026-09-02). Data as JSON: /api/errors/b0677bcd35a76ced. Report an issue: GitHub.