wavetermdev/waveterm · error

error creating listener at %v: %v

Error message

error creating listener at %v: %v

What it means

Returned by MakeRemoteUnixListener when net.Listen("unix", serverAddr) fails while creating the remote wsh domain socket. The path is derived from getRemoteDomainSocketName() and any stale socket is removed first; failure means the OS refused to bind the unix socket at that path.

Source

Thrown at cmd/wsh/cmd/wshcmd-connserver.go:127

		defer ticker.Stop()

		for range ticker.C {
			cleanupOldJobLogs()
		}
	}()
}

func getRemoteDomainSocketName() string {
	homeDir := wavebase.GetHomeDir()
	return filepath.Join(homeDir, wavebase.RemoteWaveHomeDirName, wavebase.RemoteDomainSocketBaseName)
}

func MakeRemoteUnixListener() (net.Listener, error) {
	serverAddr := getRemoteDomainSocketName()
	os.Remove(serverAddr) // ignore error
	rtn, err := net.Listen("unix", serverAddr)
	if err != nil {
		return nil, fmt.Errorf("error creating listener at %v: %v", serverAddr, err)
	}
	os.Chmod(serverAddr, 0700)
	log.Printf("Server [unix-domain] listening on %s\n", serverAddr)
	return rtn, nil
}

func handleNewListenerConn(conn net.Conn, router *wshutil.WshRouter) {
	defer func() {
		panichandler.PanicHandler("handleNewListenerConn", recover())
	}()
	var linkIdContainer atomic.Int32
	proxy := wshutil.MakeRpcProxy(fmt.Sprintf("connserver:%s", conn.RemoteAddr().String()))
	go func() {
		defer func() {
			panichandler.PanicHandler("handleNewListenerConn:AdaptOutputChToStream", recover())
		}()
		writeErr := wshutil.AdaptOutputChToStream(proxy.ToRemoteCh, conn)
		if writeErr != nil {

View on GitHub (pinned to a4447c1563)

Solutions

  1. Check whether another process is bound to the socket (`ss -x | grep <sockname>` or lsof) and kill the stale holder
  2. Ensure the socket's parent directory exists and is writable by the current user
  3. Shorten the socket path if it exceeds the ~108-char unix socket limit
  4. Remove a stale socket file manually if os.Remove failed (e.g. permissions) and retry

Example fix

// before
os.Remove(serverAddr) // ignore error
rtn, err := net.Listen("unix", serverAddr)
// after
if dir := filepath.Dir(serverAddr); dir != "." {
    os.MkdirAll(dir, 0700)
}
os.Remove(serverAddr)
rtn, err := net.Listen("unix", serverAddr)
Defensive patterns

Strategy: fallback

Validate before calling

sockDir := filepath.Dir(getRemoteDomainSocketName())
if info, err := os.Stat(sockDir); err != nil || !info.IsDir() {
    os.MkdirAll(sockDir, 0700)
}
if len(serverAddr) >= 108 {
    return fmt.Errorf("socket path too long (%d chars): %s", len(serverAddr), serverAddr)
}

Try / catch

ln, err := MakeRemoteUnixListener()
if err != nil {
    var oe *net.OpError
    if errors.As(err, &oe) && os.IsExist(oe.Err) {
        os.Remove(getRemoteDomainSocketName())
        ln, err = MakeRemoteUnixListener() // retry once after removing stale socket
    }
    if err != nil {
        return err
    }
}

Prevention

When it happens

Trigger: The socket path's parent directory does not exist, the path exceeds unix socket path limits (108 bytes), permission denied on the directory, or the address is already in use by a live process.

Common situations: Running the connserver in a container/sandbox with a read-only or missing temp dir, another wshserver instance still holding the socket, overly restrictive permissions, deeply nested paths making the socket name too long.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/5bd3e36ecc198588. Report an issue: GitHub.