{"record":{"id":"4cb225e6d8ae9480","repo":"wavetermdev/waveterm","slug":"failed-to-listen-v","errorCode":null,"errorMessage":"failed to listen: %v","messagePattern":"failed to listen: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"tsunami/engine/clientimpl.go","lineNumber":237,"sourceCode":"\t\tManifestFile: c.ManifestFileBytes,\n\t})\n\n\t// Determine listen address from environment variable or use default\n\tlistenAddr := os.Getenv(TsunamiListenAddrEnvVar)\n\tif listenAddr == \"\" {\n\t\tlistenAddr = DefaultListenAddr\n\t}\n\n\t// Create server and listen on specified address\n\tserver := &http.Server{\n\t\tAddr:    listenAddr,\n\t\tHandler: mux,\n\t}\n\n\t// Start listening\n\tlistener, err := net.Listen(\"tcp\", listenAddr)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to listen: %v\", err)\n\t}\n\n\t// Log the address we're listening on\n\tport := listener.Addr().(*net.TCPAddr).Port\n\tlog.Printf(\"[tsunami] listening at http://localhost:%d\", port)\n\n\t// Serve in a goroutine so we don't block\n\tgo func() {\n\t\tif err := server.Serve(listener); err != nil && err != http.ErrServerClosed {\n\t\t\tlog.Printf(\"HTTP server error: %v\", err)\n\t\t}\n\t}()\n\n\t// Wait for context cancellation and shutdown server gracefully\n\tgo func() {\n\t\t<-ctx.Done()\n\t\tlog.Printf(\"Context canceled, shutting down server...\")\n\t\tif err := server.Shutdown(context.Background()); err != nil {","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/tsunami/engine/clientimpl.go#L219-L255","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nlistenAddr := \"localhost:9310\" // already in use\n// after\nlistener, err := net.Listen(\"tcp\", \"localhost:0\") // OS-assigned free port","handlingStrategy":"retry","validationCode":"func portFree(addr string) bool {\n    l, err := net.Listen(\"tcp\", addr)\n    if err != nil { return false }\n    l.Close()\n    return true\n}","typeGuard":null,"tryCatchPattern":"if err := runMainE(...); err != nil {\n    if strings.Contains(err.Error(), \"failed to listen\") {\n        time.Sleep(500 * time.Millisecond) // brief backoff, then retry or pick port 0\n    }\n}","preventionTips":["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"],"tags":["go","network","tcp","listen","port-in-use"],"backgroundTag":"address-in-use","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}