chenhg5/cc-connect · error

dingtalk: marshal audio message: %w

Error message

dingtalk: marshal audio message: %w

What it means

SendAudio builds the oToMessages request body as a map and serializes it with json.Marshal; this error wraps a marshal failure. With the fixed string keys and primitive values used here (strings, an int duration), marshaling essentially cannot fail in normal operation — it is a defensive guard. Seeing it indicates corrupted data types or memory pressure.

Source

Thrown at platform/dingtalk/dingtalk.go:1265

	// This is the official API for sending voice messages in bot conversations
	token, err := p.getAccessToken()
	if err != nil {
		return fmt.Errorf("dingtalk: get access token: %w", err)
	}

	// Build oToMessages API request with sampleAudio msgKey
	// msgParam must be a JSON string, not an object
	msgParamJSON := fmt.Sprintf(`{"mediaId":"%s","duration":"%d"}`, mediaID, durationMs)
	requestBody := map[string]interface{}{
		"robotCode": p.robotCode,
		"userIds":   []string{rc.senderStaffId},
		"msgKey":    "sampleAudio",
		"msgParam":  msgParamJSON,
	}

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

	slog.Debug("dingtalk: sending voice via oToMessages API", "media_id", mediaID, "duration", durationMs, "user_id", rc.senderStaffId)

	req, err := http.NewRequestWithContext(ctx, http.MethodPost,
		"https://api.dingtalk.com/v1.0/robot/oToMessages/batchSend",
		bytes.NewReader(body))
	if err != nil {
		return fmt.Errorf("dingtalk: create audio 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: send audio request: %w", err)
	}
	defer func() { _ = resp.Body.Close() }()

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Inspect the wrapped error for the unmarshalable type
  2. Ensure all values in requestBody are JSON-encodable primitives
  3. Report a bug if it reproduces with unmodified code

Example fix

// before
requestBody := map[string]interface{}{"robotCode": code, "userIds": userIds, "msgKey": "sampleAudio", "msgParam": msgParamJSON}
// after — keep values as JSON-safe types; avoid channel/func/NaN values
requestBody := map[string]any{"robotCode": code, "userIds": []string{uid}, "msgKey": "sampleAudio", "msgParam": msgParamJSON}
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure all map values are JSON-encodable primitives
for k, v := range requestBody { if !isJSONSafe(v) { return fmt.Errorf("non-JSON-safe value at %s", k) } }

Try / catch

if err := p.SendAudio(ctx, rc, audio, format); err != nil && strings.Contains(err.Error(), "marshal audio message") { log.Fatalf("non-JSON-encodable value in request body: %v", err) }

Prevention

When it happens

Trigger: Calling SendAudio when json.Marshal fails on the requestBody map — practically only via abnormal values (e.g. someone placing an unmarshalable type like a channel or NaN in the map after code changes) or extreme memory conditions.

Common situations: Essentially never in production with stock code; appears after modifications to requestBody that add unsupported value types.

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/3783031c9c9c68f7. Report an issue: GitHub.