chenhg5/cc-connect · error

dingtalk: SendAudio: invalid reply context type %T

Error message

dingtalk: SendAudio: invalid reply context type %T

What it means

SendAudio implements the core.AudioSender interface and expects the reply-context parameter to be the platform's internal replyContext type. If the engine passes any other value (nil, or a context from a different platform), the type assertion fails and this error is returned. It signals a wiring/programming bug, not a user input problem.

Source

Thrown at platform/dingtalk/dingtalk.go:1180

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("dingtalk: send file failed: status=%d, body=%s", resp.StatusCode, string(respBody))
	}

	slog.Info("dingtalk: file message sent", "media_id", mediaID, "name", name, "user", rc.senderStaffId)
	return nil
}

var _ core.FileSender = (*Platform)(nil)

// SendAudio uploads audio bytes to DingTalk and sends a voice message.
// Implements core.AudioSender interface.
// Uses DingTalk oToMessages API with msgKey: "sampleAudio" (voice messages).
// DingTalk voice messages only support ogg/amr formats (not mp3).
func (p *Platform) SendAudio(ctx context.Context, rctx any, audio []byte, format string) error {
	rc, ok := rctx.(replyContext)
	if !ok {
		return fmt.Errorf("dingtalk: SendAudio: invalid reply context type %T", rctx)
	}

	slog.Debug("dingtalk: SendAudio called", "format", format, "size", len(audio), "conversation_id", rc.conversationId)

	// Convert MP3 to OGG if needed (DingTalk voice messages only support ogg/amr)
	if strings.ToLower(format) == "mp3" {
		slog.Debug("dingtalk: converting MP3 to OGG format (DingTalk requirement)")
		oggAudio, err := core.ConvertMP3ToOGG(ctx, audio)
		if err != nil {
			slog.Warn("dingtalk: MP3 to OGG conversion failed", "error", err)
			// Fallback: try AMR format instead
			amrAudio, err := core.ConvertMP3ToAMR(ctx, audio)
			if err != nil {
				return fmt.Errorf("dingtalk: convert MP3 to AMR failed: %w", err)
			}
			audio = amrAudio
			format = "amr"
		} else {

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Ensure the value comes from DingTalk's message-handling path (the platform constructs replyContext itself)
  2. Never pass context.Context as the reply-context argument — they are different types
  3. Check that the message originated from the DingTalk platform before calling SendAudio

Example fix

// before
err := p.SendAudio(ctx, ctx, audio, "mp3") // wrong: ctx is context.Context
// after
rc := dingtalk.ReplyContextFromMessage(msg)
err := p.SendAudio(ctx, rc, audio, "mp3")
Defensive patterns

Strategy: type-guard

Validate before calling

if rc, ok := rctx.(dingtalk.ReplyContext); !ok { return fmt.Errorf("expected dingtalk reply context, got %T", rctx) }

Type guard

func validReplyContext(rctx any) (dingtalk.ReplyContext, bool) { rc, ok := rctx.(dingtalk.ReplyContext); return rc, ok }

Try / catch

if err := p.SendAudio(ctx, rctx, audio, "mp3"); err != nil && strings.Contains(err.Error(), "invalid reply context type") { log.Errorf("wrong reply context %T passed — use the platform-provided one", rctx) }

Prevention

When it happens

Trigger: Calling SendAudio with rctx that is not of type dingtalk.replyContext — e.g. nil, a context.Context passed by mistake, or a replyContext produced by another platform adapter.

Common situations: Engine misconfiguration routing a DingTalk audio send with a foreign reply context; tests passing context.TODO(); custom code calling SendAudio directly instead of via the engine.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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