chenhg5/cc-connect · error
wps-agentspace: dial: %w
Error message
wps-agentspace: dial: %w
What it means
connect dials the platform WebSocket with a 10-second handshake timeout and wraps any dialer failure as "wps-agentspace: dial: %w". This covers DNS failure, TCP connect refusal/reset, TLS handshake errors, and HTTP-level rejection of the upgrade (non-101 status), and is retried by connectLoop.
Source
Thrown at platform/wps-agentspace/wpsagentspace.go:360
func (p *Platform) connect(ctx context.Context) error {
wsURL := p.getWSURL()
slog.Info("wps-agentspace: connecting", "url", wsURL)
header := http.Header{
"Cookie": []string{fmt.Sprintf("wps_sid=%s", p.wpsSid)},
"User-Agent": []string{"OpenClaw/Agentspace"},
"Origin": []string{"https://agentspace.wps.cn"},
}
slog.Debug("wps-agentspace: dialing with headers", "cookie_length", len(p.wpsSid))
dialer := websocket.Dialer{
HandshakeTimeout: 10 * time.Second,
}
conn, _, err := dialer.DialContext(ctx, wsURL, header)
if err != nil {
return fmt.Errorf("wps-agentspace: dial: %w", err)
}
// Set ping/pong handlers
conn.SetPingHandler(func(appData string) error {
slog.Debug("wps-agentspace: received ping")
return conn.WriteControl(websocket.PongMessage, []byte(appData), time.Now().Add(time.Second))
})
conn.SetPongHandler(func(appData string) error {
slog.Debug("wps-agentspace: received pong")
return nil
})
p.mu.Lock()
p.conn = conn
p.mu.Unlock()
// Reset backoff on successful connectionView on GitHub (pinned to 4000b2338a)
Solutions
- Unwrap and inspect: errors.Is(err, websocket.ErrBadHandshake) plus resp.StatusCode for HTTP-level rejections; net.Error Timeout for the 10s handshake timeout; *net.OpError for DNS/TCP failures.
- Verify the wsURL in config.toml points to a live endpoint: curl -i the HTTP endpoint or wscat to the same URL.
- Check network path: DNS resolution, firewall/proxy rules, and that the WPS Agentspace service is running and reachable from the host.
- Confirm TLS validity (cert not expired, correct CA); rely on connectLoop backoff retry for transient outages instead of disabling retry.
Example fix
// before: treating all dial errors the same
if err := p.connectLoop(ctx); err != nil {
slog.Error("connect failed", "err", err)
}
// after: distinguish handshake rejection from transient network errors
if err := p.connectLoop(ctx); err != nil {
if errors.Is(err, websocket.ErrBadHandshake) {
slog.Error("wps-agentspace: handshake rejected, check URL/credentials", "err", err)
} else {
slog.Warn("wps-agentspace: transient dial failure, will retry", "err", err)
}
} Defensive patterns
Strategy: retry
Validate before calling
u, err := url.Parse(cfg.WSURL)
if err != nil || (u.Scheme != "ws" && u.Scheme != "wss") {
return fmt.Errorf("invalid websocket URL %q", cfg.WSURL)
}
host := u.Hostname()
port := u.Port()
if port == "" {
port = map[bool]string{true: "443", false: "80"}[u.Scheme == "wss"]
}
if conn, err := net.DialTimeout("tcp", net.JoinHostPort(host, port), 5*time.Second); err != nil {
return fmt.Errorf("endpoint %s unreachable before dialing: %w", u.Host, err)
} else {
conn.Close()
} Try / catch
err := p.connectLoop(ctx)
var hsErr error
if errors.As(err, &hsErr) && errors.Is(hsErr, websocket.ErrBadHandshake) {
slog.Error("wps-agentspace: server rejected upgrade; check URL/auth", "err", err)
} else if err != nil {
var ne net.Error
if errors.As(err, &ne) && ne.Timeout() {
slog.Warn("wps-agentspace: handshake timed out; check network", "err", err)
}
} Prevention
- Validate the ws/wss endpoint with wscat or curl before deploying the config.
- Keep connectLoop exponential-backoff retry enabled for transient network outages.
- Monitor certificate expiry for wss:// endpoints.
- Verify proxy/firewall rules allow outbound TCP to the endpoint port; configure a proxy Dialer if behind a corporate proxy.
- Alert on repeated dial failures rather than a single one to reduce noise from blips.
When it happens
Trigger: connectLoop calling connect when: the configured ws/wss URL host is wrong or the service is down; a proxy/firewall blocks the port; DNS cannot resolve the host; TLS certificate invalid or expired; the server rejects the upgrade with 4xx/5xx; the 10s HandshakeTimeout expires on a slow network.
Common situations: Typo'd or stale WebSocket endpoint in config.toml; corporate proxy requiring CONNECT that the Dialer does not use; offline/air-gapped deployments; server restarted or rate-limiting connections; self-signed certificates without proper TLS config.
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 chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/5fd71061434ef7c0.
Report an issue: GitHub.