{"record":{"id":"3701008ed209252e","repo":"chenhg5/cc-connect","slug":"wps-agentspace-read-w","errorCode":null,"errorMessage":"wps-agentspace: read: %w","messagePattern":"wps-agentspace: read: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/wps-agentspace/wpsagentspace.go","lineNumber":459,"sourceCode":"\t\t}\n\t}\n}\n\n// readLoop processes incoming WebSocket messages.\nfunc (p *Platform) readLoop(conn *websocket.Conn, ctx context.Context) error {\n\tfor {\n\t\tif p.stopped.Load() {\n\t\t\treturn nil\n\t\t}\n\n\t\t_ = conn.SetReadDeadline(time.Now().Add(readDeadline))\n\n\t\t_, raw, err := conn.ReadMessage()\n\t\tif err != nil {\n\t\t\tif websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway) {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"wps-agentspace: read: %w\", err)\n\t\t}\n\n\t\tvar frame wsFrame\n\t\tif err := json.Unmarshal(raw, &frame); err != nil {\n\t\t\tslog.Warn(\"wps-agentspace: invalid frame\", \"error\", err)\n\t\t\tcontinue\n\t\t}\n\n\t\tif err := p.handleFrame(frame); err != nil {\n\t\t\tslog.Error(\"wps-agentspace: handle frame\", \"error\", err)\n\t\t}\n\t}\n}\n\n// handleFrame dispatches incoming frames.\nfunc (p *Platform) handleFrame(frame wsFrame) error {\n\tslog.Debug(\"wps-agentspace: received frame\", \"event\", frame.Event, \"data\", string(frame.Data))\n","sourceCodeStart":441,"sourceCodeEnd":477,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/wps-agentspace/wpsagentspace.go#L441-L477","documentation":"readLoop surfaces any WebSocket read error that is not a normal (1000) or going-away (1001) close as \"wps-agentspace: read: %w\". This terminates connect(), tearing down the session so connectLoop can reconnect with backoff. It indicates the socket broke unexpectedly: timeout (readDeadline), abrupt TCP reset, TLS termination, or an abnormal close code from the server.","triggerScenarios":"No frames arrive within readDeadline (SetReadDeadline at wpsagentspace.go:452) so ReadMessage returns a timeout; the server sends close 1011 (internal error) or an unexpected close code; the TCP connection is reset; heartbeat pings stop being answered and the server kills the link.","commonSituations":"Idle NAT/firewall dropping the WebSocket because heartbeats were insufficient; AgentSpace server restart or deploy; network interruption on the host; server closing with an abnormal close code due to auth expiry (NOT_LOGIN) mid-session.","solutions":["Read the wrapped cause: websocket.IsCloseError codes, timeouts, or connection reset each point to a different fix","Confirm the heartbeat loop is running and heartbeatPeriod is shorter than any NAT/proxy idle timeout on the path","Check whether the server sent a fatal error frame (e.g. NOT_LOGIN) before closing — re-authenticate or refresh credentials","Treat it as transient: connectLoop retries with backoff; if it recurs immediately, inspect server status and local network"],"exampleFix":"// before\nif err != nil {\n\tif websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway) {\n\t\treturn nil\n\t}\n\treturn fmt.Errorf(\"wps-agentspace: read: %w\", err)\n}\n// after\nif err != nil {\n\tif websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway) {\n\t\treturn nil\n\t}\n\tslog.Warn(\"wps-agentspace: read failed, reconnecting\", \"error\", err)\n\treturn fmt.Errorf(\"wps-agentspace: read: %w\", err)\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := p.readLoop(conn, ctx); err != nil {\n\tif websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway) {\n\t\treturn nil // clean shutdown, no reconnect needed\n\t}\n\tslog.Warn(\"read error; reconnecting with backoff\", \"error\", err)\n}","preventionTips":["Set heartbeatPeriod below any NAT/proxy idle timeout on the network path","Monitor for frequent read errors — indicates server instability or auth expiry","Make readDeadline exceed the server's ping interval","Handle fatal error frames (NOT_LOGIN) before they surface as read failures"],"tags":["websocket","network","timeout"],"backgroundTag":"websocket-connection-lost","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}