chenhg5/cc-connect · error

ffmpeg MP3 to OGG conversion failed: %w (stderr: %s)

Error message

ffmpeg MP3 to OGG conversion failed: %w (stderr: %s)

What it means

ConvertMP3ToOGG feeds MP3 bytes to ffmpeg over stdin and reads OGG from stdout; when cmd.Run() returns an error the library wraps it with the captured stderr. This indicates ffmpeg executed but rejected the job — typically undecodable MP3 data or an ffmpeg build lacking the libopus encoder. It is distinct from the missing-binary error raised earlier in the same function.

Source

Thrown at core/speech.go:426

	args := []string{
		"-i", "pipe:0",
		"-c:a", "libopus",
		"-ar", "16000",       // 16kHz sample rate for voice
		"-ac", "1",           // mono
		"-b:a", "32k",        // 32 kbps bitrate (voice quality)
		"-application", "voip", // optimize for voice
		"-f", "ogg",
		"-y",
		"pipe:1",
	}
	cmd := exec.CommandContext(ctx, ffmpegPath, args...)
	cmd.Stdin = bytes.NewReader(mp3Data)
	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr

	if err := cmd.Run(); err != nil {
		return nil, fmt.Errorf("ffmpeg MP3 to OGG conversion failed: %w (stderr: %s)", err, stderr.String())
	}
	return stdout.Bytes(), nil
}

// ConvertMP3ToAMR converts MP3 audio to AMR format using ffmpeg with stdin/stdout pipes.
// AMR format is smaller but lower quality than OGG (AMR-NB codec, 8kHz mono, 12.2kbps).
func ConvertMP3ToAMR(ctx context.Context, mp3Data []byte) ([]byte, error) {
	ffmpegPath, err := exec.LookPath("ffmpeg")
	if err != nil {
		return nil, fmt.Errorf("ffmpeg not found in PATH: %w", err)
	}

	args := []string{
		"-i", "pipe:0",
		"-c:a", "amr_nb",
		"-ar", "8000",     // 8kHz sample rate (AMR-NB standard)
		"-ac", "1",        // mono
		"-b:a", "12.2k",   // 12.2 kbps bitrate (AMR-NB max)

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Read the stderr suffix of the error — ffmpeg names the exact problem (Invalid data, Unknown encoder 'libopus').
  2. For missing libopus, install a full-featured ffmpeg build.
  3. Validate the payload is real MP3 (check for the ID3/0xFFFB sync header) before converting.
  4. If cancellation-related, increase the conversion timeout or run it earlier in the pipeline.

Example fix

// before
ogg, err := core.ConvertMP3ToOGG(ctx, raw)
// after
if len(raw) < 4 || !(bytes.HasPrefix(raw, []byte("ID3")) || raw[0] == 0xFF) {
    return fmt.Errorf("payload is not MP3")
}
ogg, err := core.ConvertMP3ToOGG(ctx, raw)
Defensive patterns

Strategy: try-catch

Validate before calling

if len(mp3) < 4 || !(bytes.HasPrefix(mp3, []byte("ID3")) || mp3[0] == 0xFF) {
    return fmt.Errorf("payload is not MP3")
}

Try / catch

ogg, err := core.ConvertMP3ToOGG(ctx, mp3Data)
if err != nil {
    log.Error("mp3->ogg failed", "stderr", extractStderr(err))
    if ctx.Err() != nil {
        return fmt.Errorf("conversion timed out: %w", ctx.Err())
    }
    return fmt.Errorf("voice conversion: %w", err)
}

Prevention

When it happens

Trigger: SendAudio (or a direct call) passes non-MP3 bytes, corrupt/truncated MP3s, an empty slice, or ctx is cancelled during conversion; also when the installed ffmpeg has no libopus encoder.

Common situations: A platform's voice download actually returns another container despite a .mp3 name; partially downloaded files; minimal ffmpeg distributions (e.g. some static builds ship without libopus); timeouts killing long conversions.

Related errors


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