wavetermdev/waveterm · error

failed to listen on domain socket: %w

Error message

failed to listen on domain socket: %w

What it means

MakeJobDomainSocket removes any stale socket at wavebase.GetRemoteJobSocketPath(jobId) and then binds a unix listener with net.Listen("unix", socketPath). This error wraps the net.Listen failure. It means the process could not bind the domain socket path — permissions, path length, directory issues, or address-in-use.

Source

Thrown at pkg/jobmanager/jobmanager.go:392

	log.Printf("StartStream: streamid=%s rwnd=%d streaming started\n", jm.pendingStreamMeta.Id, jm.pendingStreamMeta.RWnd)
	jm.pendingStreamMeta = nil
	return nil
}

func MakeJobDomainSocket(clientId string, jobId string) error {
	socketDir := filepath.Join("/tmp", fmt.Sprintf("waveterm-%d", os.Getuid()))
	err := os.MkdirAll(socketDir, 0700)
	if err != nil {
		return fmt.Errorf("failed to create socket directory: %w", err)
	}

	socketPath := wavebase.GetRemoteJobSocketPath(jobId)

	os.Remove(socketPath)

	listener, err := net.Listen("unix", socketPath)
	if err != nil {
		return fmt.Errorf("failed to listen on domain socket: %w", err)
	}

	go func() {
		defer func() {
			panichandler.PanicHandler("MakeJobDomainSocket:accept", recover())
			listener.Close()
			os.Remove(socketPath)
		}()
		for {
			conn, err := listener.Accept()
			if err != nil {
				log.Printf("error accepting connection: %v\n", err)
				return
			}
			go handleJobDomainSocketClient(conn)
		}
	}()

View on GitHub (pinned to a4447c1563)

Solutions

  1. Check for a live process holding the socket: lsof /tmp/waveterm-<uid>/<jobId> and kill the stale job
  2. Shorten the jobId/socket path if the absolute path exceeds 108 characters (EINVAL/ENAMETOOLONG)
  3. Ensure /tmp/waveterm-<uid> is owned by and writable by the current uid (0700)
  4. Manually remove the stale socket file and retry; verify the filesystem supports AF_UNIX sockets

Example fix

// before
listener, err := net.Listen("unix", socketPath)
if err != nil {
    return fmt.Errorf("failed to listen on domain socket: %w", err)
}
// after
listener, err := net.Listen("unix", socketPath)
if err != nil {
    if errors.Is(err, syscall.EADDRINUSE) {
        os.Remove(socketPath)
        listener, err = net.Listen("unix", socketPath)
    }
    if err != nil {
        return fmt.Errorf("failed to listen on domain socket %s: %w", socketPath, err)
    }
}
Defensive patterns

Strategy: retry

Validate before calling

socketPath := wavebase.GetRemoteJobSocketPath(jobId)
if len(socketPath) >= 108 {
    return fmt.Errorf("socket path too long (%d chars)", len(socketPath))
}
if fi, err := os.Lstat(socketPath); err == nil {
    os.Remove(socketPath) // stale socket
}

Try / catch

if err := MakeJobDomainSocket(clientId, jobId); err != nil {
    if strings.HasPrefix(err.Error(), "failed to listen on domain socket") {
        if strings.Contains(errors.Unwrap(err).Error(), "address already in use") {
            // find and stop the stale job process, then retry
        }
    }
    return err
}

Prevention

When it happens

Trigger: net.Listen("unix", socketPath) returns an error: socket directory unwritable, path exceeds unix socket 108-byte limit, stale socket not removable, another live process already bound (EADDRINUSE), or filesystem does not support unix sockets.

Common situations: Job id producing a path longer than sun_path (108 chars); another waveterm job process still holding the socket; /tmp mounted noexec/nodev in some setups; running as different uid than the directory owner; running in an environment without unix socket support (some sandboxes).

Related errors


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