{"record":{"id":"cd0b58cf65a9352f","repo":"chenhg5/cc-connect","slug":"sendaudio-fallback-failed-w","errorCode":null,"errorMessage":"sendAudio fallback failed: %w","messagePattern":"sendAudio fallback failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/telegram/telegram.go","lineNumber":1218,"sourceCode":"\tdefault:\n\t\tconverted, err := telegramConvertAudioToOpus(ctx, audio, sendFormat)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"telegram: SendAudio: convert %s to opus: %w\", sendFormat, err)\n\t\t}\n\t\tsendData = converted\n\t\tsendFormat = \"opus\"\n\t}\n\n\tif err := p.sendVoice(ctx, rc, sendData, sendFormat); err != nil {\n\t\tif sendFormat == \"mp3\" || sendFormat == \"m4a\" {\n\t\t\tif fallbackErr := p.sendAudio(ctx, rc, sendData, sendFormat); fallbackErr == nil {\n\t\t\t\treturn nil\n\t\t\t} else {\n\t\t\t\treturn fmt.Errorf(\n\t\t\t\t\t\"telegram: SendAudio: %w\",\n\t\t\t\t\terrors.Join(\n\t\t\t\t\t\tfmt.Errorf(\"sendVoice failed: %w\", err),\n\t\t\t\t\t\tfmt.Errorf(\"sendAudio fallback failed: %w\", fallbackErr),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\treturn fmt.Errorf(\"telegram: SendAudio: sendVoice: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc (p *Platform) sendVoice(ctx context.Context, rc replyContext, audio []byte, format string) error {\n\tbot, err := p.connectedBot(\"send voice\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tparams := &tgbot.SendVoiceParams{\n\t\tChatID:          rc.chatID,\n\t\tMessageThreadID: rc.threadID,\n\t\tVoice:           &models.InputFileUpload{Filename: \"tts_audio.\" + telegramAudioFileExt(format), Data: bytes.NewReader(audio)},","sourceCodeStart":1200,"sourceCodeEnd":1236,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/telegram/telegram.go#L1200-L1236","documentation":"This is the fallback half of the joined error returned when both SendVoice and the SendAudio fallback fail while sending a voice/audio message. It wraps the error from p.sendAudio, the fallback path used when voice sending is unsupported or fails. Its presence means Telegram also rejected the audio variant of the message.","triggerScenarios":"After sendVoice fails, p.sendAudio(ctx, rc, sendData, sendFormat) is invoked and returns a non-nil error; the code then returns fmt.Errorf(\"telegram: SendAudio: %w\", errors.Join(voiceErr, audioFallbackErr)).","commonSituations":"Audio file in a codec/container Telegram's sendAudio rejects (e.g. malformed MP3); caption or duration fields out of range; expired or wrong file_id; bot rate-limited (429).","solutions":["Read the wrapped fallbackErr text for the Bot API's specific rejection reason.","Convert the audio to a standard MP3/M4A and resend via sendAudio, or OGG/Opus for voice.","Handle HTTP 429 retry_after from the Bot API by waiting and retrying.","Check bot chat permissions and file size (50 MB bot limit)."],"exampleFix":"// before: ignoring which leg failed\nreturn err\n// after: branch on the joined causes\nif strings.Contains(err.Error(), \"sendAudio fallback failed\") { /* audio-specific handling */ }","handlingStrategy":"try-catch","validationCode":"if len(audio) == 0 { return errors.New(\"empty audio payload\") }","typeGuard":null,"tryCatchPattern":"if err := send(); err != nil {\n    var apiErr *tgbot.Error\n    if errors.As(err, &apiErr) && apiErr.Code == 429 {\n        time.Sleep(time.Duration(apiErr.RetryAfter) * time.Second)\n    }\n}","preventionTips":["Validate audio codec/container before upload","Handle 429 retry_after responses","Confirm bot membership in target chats","Log joined errors separately for diagnosis"],"tags":["telegram","bot-api","audio-upload","fallback"],"backgroundTag":"api-request-failed","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}