wavetermdev/waveterm · critical
failed to listen: %v
Error message
failed to listen: %v
What it means
ClientImpl.listenAndServe opens a TCP listener for the client's HTTP endpoint (with the given mux). If net.Listen fails — address already bound, permission denied, or invalid address — it aborts with this wrapped error.
Source
Thrown at tsunami/engine/clientimpl.go:237
ManifestFile: c.ManifestFileBytes,
})
// Determine listen address from environment variable or use default
listenAddr := os.Getenv(TsunamiListenAddrEnvVar)
if listenAddr == "" {
listenAddr = DefaultListenAddr
}
// Create server and listen on specified address
server := &http.Server{
Addr: listenAddr,
Handler: mux,
}
// Start listening
listener, err := net.Listen("tcp", listenAddr)
if err != nil {
return fmt.Errorf("failed to listen: %v", err)
}
// Log the address we're listening on
port := listener.Addr().(*net.TCPAddr).Port
log.Printf("[tsunami] listening at http://localhost:%d", port)
// Serve in a goroutine so we don't block
go func() {
if err := server.Serve(listener); err != nil && err != http.ErrServerClosed {
log.Printf("HTTP server error: %v", err)
}
}()
// Wait for context cancellation and shutdown server gracefully
go func() {
<-ctx.Done()
log.Printf("Context canceled, shutting down server...")
if err := server.Shutdown(context.Background()); err != nil {View on GitHub (pinned to a4447c1563)
Solutions
- Stop the process already bound to the port (lsof -i :PORT / kill) or pick a free port
- Use port 0 to let the OS pick a free port (the code logs the actual bound port)
- Run with sufficient privileges if binding a privileged port, or choose a high port
- Validate listenAddr syntax (host:port) in configuration
Example fix
// before
listenAddr := "localhost:9310" // already in use
// after
listener, err := net.Listen("tcp", "localhost:0") // OS-assigned free port Defensive patterns
Strategy: retry
Validate before calling
func portFree(addr string) bool {
l, err := net.Listen("tcp", addr)
if err != nil { return false }
l.Close()
return true
} Try / catch
if err := runMainE(...); err != nil {
if strings.Contains(err.Error(), "failed to listen") {
time.Sleep(500 * time.Millisecond) // brief backoff, then retry or pick port 0
}
} Prevention
- Bind port 0 (ephemeral) in dev/CI to avoid conflicts
- Check for stale processes with lsof -i :PORT before start
- Add process/port cleanup to shutdown paths
- Avoid privileged ports (<1024) for unprivileged runs
When it happens
Trigger: net.Listen("tcp", listenAddr) returning an error: another instance already listening on the port (EADDRINUSE), privileged port without root, malformed listen address, or IPv6 unavailability.
Common situations: Starting a second tsunami client while the first still runs; leftover process holding the port after a crash; Docker/CI port conflicts; running on port <1024 as non-root.
Related errors
- wcloud endpoint not set
- wcloud ping endpoint not set
- %s
- failed to reconnect to job manager: %w
- failed to listen on domain socket: %w
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/4cb225e6d8ae9480.
Report an issue: GitHub.