{"record":{"id":"eb3b7ecde3b21ece","repo":"chenhg5/cc-connect","slug":"s-convert-to-opus-w","errorCode":null,"errorMessage":"%s: convert to opus: %w","messagePattern":"(.+?): convert to opus: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/feishu/feishu.go","lineNumber":5473,"sourceCode":"\t\t\t}\n\t\t\treturn nil\n\t\t})\n\t})\n}\n\n// SendAudio uploads audio bytes to Feishu and sends a voice message.\n// Implements core.AudioSender interface.\n// Feishu audio messages require opus format; non-opus input is converted via ffmpeg.\nfunc (p *Platform) SendAudio(ctx context.Context, rctx any, audio []byte, format string) error {\n\trc, ok := rctx.(replyContext)\n\tif !ok {\n\t\treturn fmt.Errorf(\"%s: SendAudio: invalid reply context type %T\", p.tag(), rctx)\n\t}\n\n\tif format != \"opus\" {\n\t\tconverted, err := core.ConvertAudioToOpus(ctx, audio, format)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"%s: convert to opus: %w\", p.tag(), err)\n\t\t}\n\t\taudio = converted\n\t\tformat = \"opus\"\n\t}\n\n\tvar uploadResp *larkim.CreateFileResp\n\tif err := p.withTransientRetry(ctx, \"upload audio\", func() error {\n\t\treturn p.withFreshTenantAccessTokenRetry(ctx, \"upload audio\", func(client *lark.Client, options ...larkcore.RequestOptionFunc) error {\n\t\t\treq := larkim.NewCreateFileReqBuilder().\n\t\t\t\tBody(larkim.NewCreateFileReqBodyBuilder().\n\t\t\t\t\tFileType(larkim.FileTypeOpus).\n\t\t\t\t\tFileName(\"tts_audio.opus\").\n\t\t\t\t\tFile(bytes.NewReader(audio)).\n\t\t\t\t\tBuild()).\n\t\t\t\tBuild()\n\t\t\tvar err error\n\t\t\tuploadResp, err = client.Im.File.Create(ctx, req, options...)\n\t\t\tif err != nil {","sourceCodeStart":5455,"sourceCodeEnd":5491,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/feishu/feishu.go#L5455-L5491","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Install ffmpeg and confirm it is on PATH (ffmpeg -version)","Check the input audio is valid and matches the declared format","Send audio already encoded as opus to skip conversion","Wrap the cause (%w) to see ffmpeg's stderr for the exact failure"],"exampleFix":"// before\nerr := p.SendAudio(ctx, rc, ttsOutput, \"mp3\")\n// after\nopus, err := core.ConvertAudioToOpus(ctx, ttsOutput, \"mp3\") // surface the ffmpeg error early\nif err != nil { slog.Error(\"audio conversion failed\", \"err\", err) } else { p.SendAudio(ctx, rc, opus, \"opus\") }","handlingStrategy":"fallback","validationCode":"// check ffmpeg availability at startup\nif _, err := exec.LookPath(\"ffmpeg\"); err != nil { slog.Warn(\"ffmpeg not found; audio conversion will fail\") }","typeGuard":null,"tryCatchPattern":"converted, err := core.ConvertAudioToOpus(ctx, audio, format)\nif err != nil {\n    slog.Error(\"opus conversion failed\", \"err\", err)\n    return fallbackSendAsFile(ctx, audio, format) // degrade gracefully\n}","preventionTips":["Install ffmpeg in the deployment image (verify with ffmpeg -version)","Validate audio bytes are non-empty before sending","Prefer opus input to skip conversion","Check ffmpeg stderr in the wrapped error"],"tags":["feishu","audio","ffmpeg","conversion"],"backgroundTag":"missing-optional-dependency","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"}