chenhg5/cc-connect · error

wps-agentspace: marshal: %w

Error message

wps-agentspace: marshal: %w

What it means

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).

Source

Thrown at platform/wps-agentspace/wpsagentspace.go:649

// sendTyping sends a typing indicator.
func (p *Platform) sendTyping(chatID string) {
	_ = p.writeJSON("typing", typingData{
		ChatID:     chatID,
		DeviceUUID: p.deviceUuid,
		DeviceName: p.deviceName,
		Timestamp:  time.Now().UnixMilli(),
	})
}

// writeJSON sends a JSON frame through the write channel.
func (p *Platform) writeJSON(event string, data any) error {
	frame := wsFrame{
		Event: event,
	}
	jsonData, err := json.Marshal(data)
	if err != nil {
		return fmt.Errorf("wps-agentspace: marshal: %w", err)
	}
	frame.Data = jsonData

	raw, err := json.Marshal(frame)
	if err != nil {
		return fmt.Errorf("wps-agentspace: marshal frame: %w", err)
	}

	select {
	case p.writeCh <- raw:
		return nil
	default:
		return fmt.Errorf("wps-agentspace: write buffer full")
	}
}

// writeLoop serializes all WebSocket writes.
func (p *Platform) writeLoop(conn *websocket.Conn) {

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Inspect the inner error (%w) to find the offending field/type at the given Go path
  2. Remove or make serializable any func/chan/cyclic fields in the payload struct
  3. Add a MarshalJSON method to custom types passed into sendText/sendTyping
  4. Log the data value's type at the call site to identify what fails to marshal

Example fix

// before
p.sendText(chatID, someStructWithChan{done: make(chan struct{})})
// after
payload := struct{ ChatID string `json:"chat_id"`; Text string `json:"text"` }{ChatID: chatID, Text: text}
p.sendText(chatID, payload)
Defensive patterns

Strategy: validation

Validate before calling

func isJSONSerializable(v any) error { _, err := json.Marshal(v); return err }
if err := isJSONSerializable(payload); err != nil { log.Fatalf("payload not serializable: %v", err) }

Try / catch

if err := p.sendText(chatID, text); err != nil { if strings.Contains(err.Error(), "marshal:") { slog.Error("payload serialization failed", "err", err) } }

Prevention

When it happens

Trigger: Calling sendInit, heartbeatLoop, handleFrame, sendText, or sendTyping with a data value containing unsupported types (chan, func, complex) or a cyclic structure passed to writeJSON.

Common situations: 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.

Understand the failure class

Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/873ff196b21fd393. Report an issue: GitHub.