chenhg5/cc-connect · error

marshal: %w

Error message

marshal: %w

What it means

The lspWriter failed to JSON-marshal a JSON-RPC message (request, notification, response, or error) before framing it with Content-Length. json.Marshal fails only for unsupported types (channels, funcs, cyclic structures), so this indicates a bug in message construction rather than environment issues.

Source

Thrown at agent/copilot/jsonrpc.go:61

func (e *jsonRPCError) Error() string {
	return fmt.Sprintf("JSON-RPC error %d: %s", e.Code, e.Message)
}

// lspWriter writes Content-Length framed JSON-RPC messages.
type lspWriter struct {
	w  io.Writer
	mu sync.Mutex
}

func newLSPWriter(w io.Writer) *lspWriter {
	return &lspWriter{w: w}
}

func (lw *lspWriter) writeMessage(v any) error {
	data, err := json.Marshal(v)
	if err != nil {
		return fmt.Errorf("marshal: %w", err)
	}

	header := fmt.Sprintf("Content-Length: %d\r\n\r\n", len(data))

	lw.mu.Lock()
	defer lw.mu.Unlock()

	if _, err := io.WriteString(lw.w, header); err != nil {
		return fmt.Errorf("write header: %w", err)
	}
	if _, err := lw.w.Write(data); err != nil {
		return fmt.Errorf("write body: %w", err)
	}
	return nil
}

// lspReader reads Content-Length framed JSON-RPC messages.
type lspReader struct {

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Inspect the value being marshaled in the failing call and remove/replace non-marshalable fields
  2. Add json:"-" tags to fields that must not be serialized
  3. Convert custom types to plain serializable structs/maps before calling call/notify
  4. Add a unit test marshaling every JSON-RPC message type the agent sends

Example fix

// before
type deleteParams struct {
    SessionID string `json:"sessionId"`
    onClose func() // not marshalable
}
// after
type deleteParams struct {
    SessionID string `json:"sessionId"`
    onClose func() `json:"-"`
}
Defensive patterns

Strategy: validation

Validate before calling

// guard helper for JSON-RPC payloads
func mustMarshalable(v any) error {
    var b bytes.Buffer
    enc := json.NewEncoder(&b)
    if err := enc.Encode(v); err != nil { return fmt.Errorf("unmarshalable rpc payload: %w", err) }
    return nil
}

Try / catch

if err := rpc.call(ctx, method, params); err != nil && strings.Contains(err.Error(), "marshal:") {
    slog.Error("non-serializable RPC payload — bug in params construction", "method", method, "err", err)
}

Prevention

When it happens

Trigger: call/notify/respond/writeResponse/writeError passing a params or result value containing a non-marshalable type (chan, func, or a reference cycle) to writeMessage.

Common situations: After a code change introducing a custom params type with unsupported fields (e.g. sync.Mutex captured by value, func field, cyclic pointer graph).

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/3ad632519fed71e3. Report an issue: GitHub.