{"record":{"id":"8c24c48222fa4023","repo":"chenhg5/cc-connect","slug":"ffmpeg-opus-conversion-failed-w-stderr-s","errorCode":null,"errorMessage":"ffmpeg opus conversion failed: %w (stderr: %s)","messagePattern":"ffmpeg opus conversion failed: %w \\(stderr: (.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/speech.go","lineNumber":361,"sourceCode":"// Returns the opus bytes. If ffmpeg is not installed, returns an error.\nfunc ConvertAudioToOpus(ctx context.Context, audio []byte, srcFormat string) ([]byte, error) {\n\tffmpegPath, err := exec.LookPath(\"ffmpeg\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"ffmpeg not found in PATH: install ffmpeg to enable audio conversion\")\n\t}\n\n\targs := []string{\"-i\", \"pipe:0\", \"-c:a\", \"libopus\", \"-f\", \"opus\", \"-y\", \"pipe:1\"}\n\tif srcFormat == \"amr\" || srcFormat == \"silk\" {\n\t\targs = append([]string{\"-f\", srcFormat}, args...)\n\t}\n\tcmd := exec.CommandContext(ctx, ffmpegPath, args...)\n\tcmd.Stdin = bytes.NewReader(audio)\n\tvar stdout, stderr bytes.Buffer\n\tcmd.Stdout = &stdout\n\tcmd.Stderr = &stderr\n\n\tif err := cmd.Run(); err != nil {\n\t\treturn nil, fmt.Errorf(\"ffmpeg opus conversion failed: %w (stderr: %s)\", err, stderr.String())\n\t}\n\treturn stdout.Bytes(), nil\n}\n\n// ConvertAudioToAMR uses ffmpeg to convert audio to AMR-NB format.\n// AMR is a common voice codec for mobile messaging platforms.\n// Returns the AMR bytes. If ffmpeg is not installed, returns an error.\nfunc ConvertAudioToAMR(ctx context.Context, audio []byte, srcFormat string) ([]byte, error) {\n\tffmpegPath, err := exec.LookPath(\"ffmpeg\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"ffmpeg not found in PATH: install ffmpeg to enable audio conversion\")\n\t}\n\n\targs := []string{\n\t\t\"-i\", \"pipe:0\",\n\t\t\"-c:a\", \"amr_nb\",\n\t\t\"-ar\", \"8000\",   // 8kHz sample rate (AMR-NB standard)\n\t\t\"-ac\", \"1\",      // mono","sourceCodeStart":343,"sourceCodeEnd":379,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/speech.go#L343-L379","documentation":"ConvertAudioToOpus runs ffmpeg as a subprocess and, if cmd.Run() returns an error, wraps it together with the captured stderr. The wrap message distinguishes this runtime failure from the missing-binary case: ffmpeg was found and executed but the opus conversion failed. The stderr text is the key diagnostic — it holds ffmpeg's own parse/codec errors.","triggerScenarios":"SendAudio (or a direct call) passes audio bytes ffmpeg cannot decode or cannot encode to libopus: corrupt/truncated input, srcFormat mislabeled (e.g. bytes declared \"amr\" that are actually wav), empty input, ffmpeg build without libopus encoder, or ctx cancellation killing the process mid-run.","commonSituations":"Platform delivers an unusual voice codec the local ffmpeg lacks a decoder for; a stripped ffmpeg build (e.g. libavcodec without libopus); context deadline too short for long voice notes; user uploads a corrupted file.","solutions":["Inspect the stderr in the error message — it names the failing stage (Input/Output error, Unknown encoder 'libopus', etc.).","If it says libopus is unknown, install a full ffmpeg build that includes libopus.","Confirm srcFormat matches the actual input codec; fix the format detection on the caller side.","If caused by ctx cancellation, raise the timeout; if input is empty or truncated, re-fetch or reject the file."],"exampleFix":"// before\nopus, err := core.ConvertAudioToOpus(ctx, voiceBytes, \"amr\")\n// after\nif !bytes.HasPrefix(voiceBytes, []byte(\"#!AMR\")) {\n    return fmt.Errorf(\"expected AMR payload, got different format\")\n}\nopus, err := core.ConvertAudioToOpus(ctx, voiceBytes, \"amr\")","handlingStrategy":"try-catch","validationCode":"if len(audio) == 0 {\n    return fmt.Errorf(\"empty audio payload\")\n}\n","typeGuard":null,"tryCatchPattern":"opus, err := core.ConvertAudioToOpus(ctx, audio, srcFormat)\nif err != nil {\n    log.Error(\"opus conversion failed\", \"stderr\", extractStderr(err), \"format\", srcFormat)\n    return fmt.Errorf(\"send voice note: %w\", err)\n}","preventionTips":["Validate srcFormat against the actual container magic bytes before converting.","Use an ffmpeg build that includes libopus (verify with `ffmpeg -encoders | grep libopus`).","Set an adequate context timeout proportional to audio duration.","Log the stderr portion on failure — it is the authoritative diagnostic."],"tags":["ffmpeg","audio","subprocess","opus","conversion-failed"],"backgroundTag":"ffmpeg-conversion-failed","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}