chenhg5/cc-connect · error

%s: convert to opus: %w

Error message

%s: convert to opus: %w

What it means

Before sending a voice message, SendAudio must convert non-opus audio to Feishu's required opus format using core.ConvertAudioToOpus (which shells out to ffmpeg). This error wraps a conversion failure. It means ffmpeg is missing, the input bytes are not valid audio in the claimed format, or the conversion process failed/timed out.

Source

Thrown at platform/feishu/feishu.go:5473

			}
			return nil
		})
	})
}

// SendAudio uploads audio bytes to Feishu and sends a voice message.
// Implements core.AudioSender interface.
// Feishu audio messages require opus format; non-opus input is converted via ffmpeg.
func (p *Platform) SendAudio(ctx context.Context, rctx any, audio []byte, format string) error {
	rc, ok := rctx.(replyContext)
	if !ok {
		return fmt.Errorf("%s: SendAudio: invalid reply context type %T", p.tag(), rctx)
	}

	if format != "opus" {
		converted, err := core.ConvertAudioToOpus(ctx, audio, format)
		if err != nil {
			return fmt.Errorf("%s: convert to opus: %w", p.tag(), err)
		}
		audio = converted
		format = "opus"
	}

	var uploadResp *larkim.CreateFileResp
	if err := p.withTransientRetry(ctx, "upload audio", func() error {
		return p.withFreshTenantAccessTokenRetry(ctx, "upload audio", func(client *lark.Client, options ...larkcore.RequestOptionFunc) error {
			req := larkim.NewCreateFileReqBuilder().
				Body(larkim.NewCreateFileReqBodyBuilder().
					FileType(larkim.FileTypeOpus).
					FileName("tts_audio.opus").
					File(bytes.NewReader(audio)).
					Build()).
				Build()
			var err error
			uploadResp, err = client.Im.File.Create(ctx, req, options...)
			if err != nil {

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Install ffmpeg and confirm it is on PATH (ffmpeg -version)
  2. Check the input audio is valid and matches the declared format
  3. Send audio already encoded as opus to skip conversion
  4. Wrap the cause (%w) to see ffmpeg's stderr for the exact failure

Example fix

// before
err := p.SendAudio(ctx, rc, ttsOutput, "mp3")
// after
opus, err := core.ConvertAudioToOpus(ctx, ttsOutput, "mp3") // surface the ffmpeg error early
if err != nil { slog.Error("audio conversion failed", "err", err) } else { p.SendAudio(ctx, rc, opus, "opus") }
Defensive patterns

Strategy: fallback

Validate before calling

// check ffmpeg availability at startup
if _, err := exec.LookPath("ffmpeg"); err != nil { slog.Warn("ffmpeg not found; audio conversion will fail") }

Try / catch

converted, err := core.ConvertAudioToOpus(ctx, audio, format)
if err != nil {
    slog.Error("opus conversion failed", "err", err)
    return fallbackSendAsFile(ctx, audio, format) // degrade gracefully
}

Prevention

When it happens

Trigger: format != "opus" and ConvertAudioToOpus returns an error: ffmpeg binary not on PATH, corrupt/empty audio bytes, unsupported input container/codec, or the conversion command exits non-zero.

Common situations: Docker images without ffmpeg installed; agents returning webm/ogg streams mislabeled as mp3/wav; zero-length audio from a TTS tool; ffmpeg version too old for the requested flags.

Understand the failure class

Background: "X is not installed. Please install it with pip install Y": missing optional dependency errors — ImportError/ValueError raised when a library's optional extra was never installed — 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/eb3b7ecde3b21ece. Report an issue: GitHub.