chenhg5/cc-connect · error

dingtalk: marshal proactive message: %w

Error message

dingtalk: marshal proactive message: %w

What it means

This error wraps a json.Marshal failure while serializing the proactive message payload (robotCode, userIds/conversationId, msgKey, msgParam) into the HTTP request body. It indicates the request struct could not be converted to JSON, which for these plain types almost always means an unexpected value ended up in the payload (e.g. an unsupported type slipped into msgParam or a nested field).

Source

Thrown at platform/dingtalk/dingtalk.go:1680

			"msgParam":           string(msgParam),
		}
	} else if rc.senderStaffId != "" {
		// Direct message via /v1.0/robot/oToMessages/batchSend
		apiURL = "https://api.dingtalk.com/v1.0/robot/oToMessages/batchSend"
		msgParam, _ := json.Marshal(map[string]string{"title": cardTitleFromContent(content), "text": content})
		requestBody = map[string]any{
			"robotCode": p.robotCode,
			"userIds":   []string{rc.senderStaffId},
			"msgKey":    "sampleMarkdown",
			"msgParam":  string(msgParam),
		}
	} else {
		return fmt.Errorf("dingtalk: proactive send requires conversationId (group) or senderStaffId (direct)")
	}

	body, err := json.Marshal(requestBody)
	if err != nil {
		return fmt.Errorf("dingtalk: marshal proactive message: %w", err)
	}

	req, err := http.NewRequestWithContext(ctx, http.MethodPost, apiURL, bytes.NewReader(body))
	if err != nil {
		return fmt.Errorf("dingtalk: create proactive request: %w", err)
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("x-acs-dingtalk-access-token", token)

	resp, err := p.httpClient.Do(req)
	if err != nil {
		return fmt.Errorf("dingtalk: proactive send request: %w", err)
	}
	defer func() { _ = resp.Body.Close() }()

	respBody, _ := io.ReadAll(resp.Body)
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("dingtalk: proactive send failed: status=%d, body=%s", resp.StatusCode, string(respBody))

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Inspect the wrapped error (%w) to see the exact Go type that failed to marshal.
  2. Ensure msgParam is produced via json.Marshal of a plain map/struct (string), not raw dynamic values.
  3. Log the requestBody structure (with token redaction) in a debug build to find the offending field.
  4. If the marshal error persists with plain types, report it as a bug — stdlib json.Marshal cannot fail on map[string]any of strings/slices.

Example fix

// before
msgParam, _ := somethingDynamic() // may be non-serializable
requestBody["msgParam"] = msgParam
// after
msgParamBytes, err := json.Marshal(map[string]string{"content": content})
if err != nil {
    return fmt.Errorf("dingtalk: build msgParam: %w", err)
}
requestBody["msgParam"] = string(msgParamBytes)
Defensive patterns

Strategy: try-catch

Validate before calling

if _, err := json.Marshal(requestBody); err != nil {
    slog.Error("payload not serializable", "err", err)
}

Try / catch

if err := p.Send(ctx, rc, msg); err != nil {
    if strings.Contains(err.Error(), "marshal proactive message") {
        slog.Error("proactive payload invalid", "err", err)
    }
}

Prevention

When it happens

Trigger: json.Marshal(requestBody) returns an error immediately after building the proactive-send request map in the proactive send function.

Common situations: Embedding a non-JSON-serializable value (channel, func, cyclic structure) into the request body via custom options; corrupted msgParam bytes assembly upstream.

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/2125c938faa395dd. Report an issue: GitHub.