wavetermdev/waveterm · error

failed to connect to tcp or unix domain socket: tcp err:%w:

Error message

failed to connect to tcp or unix domain socket: tcp err:%w: unix socket err: %w

What it means

SetupDomainSocketRpcClient first tries a TCP dial to the path and falls back to a unix domain socket dial. If BOTH attempts fail, it combines both underlying errors into this single wrapped error, meaning nothing is listening at (or reachable via) the given socket address.

Source

Thrown at pkg/wshutil/wshutil.go:203

	return net.DialTCP("tcp", nil, addr)
}

func SetupDomainSocketRpcClient(sockName string, serverImpl ServerImpl, debugName string) (*WshRpc, error) {
	sockName = wavebase.ExpandHomeDirSafe(sockName)
	resolvedPath, err := filepath.EvalSymlinks(sockName)
	if err == nil {
		sockName = resolvedPath
	}
	if !filepath.IsAbs(sockName) {
		return nil, fmt.Errorf("socket path must be absolute: %s", sockName)
	}
	conn, tcpErr := tryTcpSocket(sockName)
	var unixErr error
	if tcpErr != nil {
		conn, unixErr = net.Dial("unix", sockName)
	}
	if tcpErr != nil && unixErr != nil {
		return nil, fmt.Errorf("failed to connect to tcp or unix domain socket: tcp err:%w: unix socket err: %w", tcpErr, unixErr)
	}
	rtn, errCh, err := SetupConnRpcClient(conn, serverImpl, debugName)
	go func() {
		defer func() {
			panichandler.PanicHandler("SetupDomainSocketRpcClient:closeConn", recover())
		}()
		defer conn.Close()
		err := <-errCh
		if err != nil && err != io.EOF {
			log.Printf("error in domain socket connection: %v\n", err)
		}
	}()
	return rtn, err
}

func MakeClientJWTToken(rpcCtx wshrpc.RpcContext) (string, error) {
	if wavebase.IsDevMode() {
		if rpcCtx.IsRouter && (rpcCtx.RouteId != "" || rpcCtx.ProcRoute) {

View on GitHub (pinned to a4447c1563)

Solutions

  1. Confirm the server process is running and has created the socket file (ls -l on the path).
  2. Remove stale socket files and restart the server.
  3. Verify the socket path matches what the server actually listens on.
  4. Check permissions on the socket file and its parent directories.
  5. Read the wrapped tcpErr/unixErr details — ECONNREFUSED vs ENOENT vs EACCES point to different fixes.

Example fix

// before
conn, _, err := wshutil.SetupDomainSocketRpcClient(ctx, impl, sock, "dbg")
if err != nil { return err }
// after
conn, _, err := wshutil.SetupDomainSocketRpcClient(ctx, impl, sock, "dbg")
if err != nil {
    if errors.Is(err, os.ErrNotExist) {
        return fmt.Errorf("server not running at %s: %w", sock, err)
    }
    return err
}
Defensive patterns

Strategy: retry

Validate before calling

if _, err := os.Stat(sockName); err != nil {
    return fmt.Errorf("socket file %s not found; is the server running?", sockName)
}

Try / catch

var conn *wshutil.WshConn
var err error
for i := 0; i < 5; i++ {
    conn, _, err = SetupDomainSocketRpcClient(ctx, impl, sockName, dbg)
    if err == nil { break }
    time.Sleep(time.Duration(1<<i) * 100 * time.Millisecond)
}
if err != nil { return fmt.Errorf("server unreachable at %s: %w", sockName, err) }

Prevention

When it happens

Trigger: Neither tryTcpSocket(sockName) nor net.Dial("unix", sockName) succeeds — the socket file does not exist, the server process is not running, stale socket file after a crash, or permission denied on the socket.

Common situations: Wave server not started yet; leftover stale socket file; wrong socket path in config; permissions changed on the socket directory; server listening on a different version's socket path.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


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