{"record":{"id":"873ff196b21fd393","repo":"chenhg5/cc-connect","slug":"wps-agentspace-marshal-w","errorCode":null,"errorMessage":"wps-agentspace: marshal: %w","messagePattern":"wps-agentspace: marshal: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/wps-agentspace/wpsagentspace.go","lineNumber":649,"sourceCode":"\n// sendTyping sends a typing indicator.\nfunc (p *Platform) sendTyping(chatID string) {\n\t_ = p.writeJSON(\"typing\", typingData{\n\t\tChatID:     chatID,\n\t\tDeviceUUID: p.deviceUuid,\n\t\tDeviceName: p.deviceName,\n\t\tTimestamp:  time.Now().UnixMilli(),\n\t})\n}\n\n// writeJSON sends a JSON frame through the write channel.\nfunc (p *Platform) writeJSON(event string, data any) error {\n\tframe := wsFrame{\n\t\tEvent: event,\n\t}\n\tjsonData, err := json.Marshal(data)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"wps-agentspace: marshal: %w\", err)\n\t}\n\tframe.Data = jsonData\n\n\traw, err := json.Marshal(frame)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"wps-agentspace: marshal frame: %w\", err)\n\t}\n\n\tselect {\n\tcase p.writeCh <- raw:\n\t\treturn nil\n\tdefault:\n\t\treturn fmt.Errorf(\"wps-agentspace: write buffer full\")\n\t}\n}\n\n// writeLoop serializes all WebSocket writes.\nfunc (p *Platform) writeLoop(conn *websocket.Conn) {","sourceCodeStart":631,"sourceCodeEnd":667,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/wps-agentspace/wpsagentspace.go#L631-L667","documentation":"writeJSON marshals the data payload to JSON before wrapping it in a wsFrame; if json.Marshal fails on the payload it returns this wrapped error. It means the data passed to the WebSocket sender is not JSON-serializable (e.g. channels, funcs, cyclic structures).","triggerScenarios":"Calling sendInit, heartbeatLoop, handleFrame, sendText, or sendTyping with a data value containing unsupported types (chan, func, complex) or a cyclic structure passed to writeJSON.","commonSituations":"A developer extends the platform and passes a custom struct with unserializable fields (e.g. a sync.Mutex is fine, but a func field or circular pointer) into sendText/sendTyping.","solutions":["Inspect the inner error (%w) to find the offending field/type at the given Go path","Remove or make serializable any func/chan/cyclic fields in the payload struct","Add a MarshalJSON method to custom types passed into sendText/sendTyping","Log the data value's type at the call site to identify what fails to marshal"],"exampleFix":"// before\np.sendText(chatID, someStructWithChan{done: make(chan struct{})})\n// after\npayload := struct{ ChatID string `json:\"chat_id\"`; Text string `json:\"text\"` }{ChatID: chatID, Text: text}\np.sendText(chatID, payload)","handlingStrategy":"validation","validationCode":"func isJSONSerializable(v any) error { _, err := json.Marshal(v); return err }\nif err := isJSONSerializable(payload); err != nil { log.Fatalf(\"payload not serializable: %v\", err) }","typeGuard":null,"tryCatchPattern":"if err := p.sendText(chatID, text); err != nil { if strings.Contains(err.Error(), \"marshal:\") { slog.Error(\"payload serialization failed\", \"err\", err) } }","preventionTips":["Keep WebSocket payload structs free of func/chan/cyclic fields","Add a unit test that marshals every outbound payload type","Prefer explicit DTO structs over passing arbitrary values"],"tags":["websocket","json","serialization"],"backgroundTag":"json-marshal-failed","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}