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
- Check for a live process holding the socket: lsof /tmp/waveterm-<uid>/<jobId> and kill the stale job
- Shorten the jobId/socket path if the absolute path exceeds 108 characters (EINVAL/ENAMETOOLONG)
- Ensure /tmp/waveterm-<uid> is owned by and writable by the current uid (0700)
- 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
- Keep job ids short so the socket path stays under 108 characters
- Always unlink stale sockets before binding (the code does; keep that behavior)
- Detect leftover job processes with lsof/ss before restart
- Verify unix socket support and permissions in sandboxed/container environments
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
- error creating listener at %v: %v
- failed to create socket directory: %w
- failed to connect to tcp or unix domain socket: tcp err:%w:
- wcloud endpoint not set
- wcloud ping endpoint not set
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/a147b8077a2a2b77.
Report an issue: GitHub.