{"record":{"id":"4c8b64b176d4ff1f","repo":"chenhg5/cc-connect","slug":"synthesize-w","errorCode":null,"errorMessage":"synthesize: %w","messagePattern":"synthesize: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/engine.go","lineNumber":15889,"sourceCode":"\tif e.tts.TTS == nil {\n\t\treturn fmt.Errorf(\"tts provider is not configured\")\n\t}\n\tif e.tts.MaxTextLen > 0 && utf8.RuneCountInString(text) > e.tts.MaxTextLen {\n\t\treturn fmt.Errorf(\"text exceeds max_text_len (%d > %d)\", utf8.RuneCountInString(text), e.tts.MaxTextLen)\n\t}\n\tas, ok := p.(AudioSender)\n\tif !ok {\n\t\treturn fmt.Errorf(\"platform %s does not support audio sending\", p.Name())\n\t}\n\tslog.Info(\"tts: starting synthesis\", \"voice\", e.tts.Voice, \"speed\", e.tts.Speed, \"text_len\", len(text))\n\topts := TTSSynthesisOpts{\n\t\tVoice:        e.tts.Voice,\n\t\tLanguageType: e.tts.LanguageType,\n\t\tSpeed:        e.tts.Speed,\n\t}\n\taudioData, format, err := e.tts.TTS.Synthesize(e.ctx, StripMarkdown(text), opts)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"synthesize: %w\", err)\n\t}\n\tslog.Info(\"tts: synthesis successful\", \"format\", format, \"audio_size\", len(audioData))\n\tif err := as.SendAudio(e.ctx, replyCtx, audioData, format); err != nil {\n\t\treturn fmt.Errorf(\"send audio: %w\", err)\n\t}\n\tslog.Info(\"tts: audio sent successfully\", \"platform\", p.Name())\n\treturn nil\n}\n\n// ──────────────────────────────────────────────────────────────\n// Bot-to-bot relay\n// ──────────────────────────────────────────────────────────────\n\ntype platformNameOnly struct {\n\tname string\n}\n\nfunc (p platformNameOnly) Name() string                           { return p.name }","sourceCodeStart":15871,"sourceCodeEnd":15907,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/engine.go#L15871-L15907","documentation":"cc-connect wraps any failure from the configured TTS engine's Synthesize call (which converts reply text into audio data) with the 'synthesize: %w' prefix. The error is a pass-through wrapper: the underlying cause (network, auth, quota, unsupported text) is preserved via %w and should be inspected with errors.Unwrap or errors.As. It is thrown in the engine's TTS reply path before any audio is sent to the platform.","triggerScenarios":"A message reply triggers text-to-speech (e.tts configured) and e.tts.TTS.Synthesize(e.ctx, StripMarkdown(text), opts) returns a non-nil error — e.g. the TTS provider API is unreachable, credentials are invalid, the text exceeds provider limits, or the context is cancelled mid-request.","commonSituations":"Misconfigured TTS provider credentials in config.toml; TTS service outage or rate limiting; text length exceeding the provider's max input; network egress blocked in the deployment environment; e.ctx cancelled because the user stopped the bot.","solutions":["Check the wrapped cause with errors.Unwrap(err) or %v of the error to see the provider-specific failure","Verify TTS provider credentials/endpoint in the [tts] section of config.toml","Test the TTS provider directly (curl / SDK sample) to rule out outage or quota limits","Check network egress/proxy settings on the host running cc-connect","Retry — transient network or rate-limit failures resolve on their own"],"exampleFix":"// before\nif err != nil {\n    return fmt.Errorf(\"synthesize: %w\", err)\n}\n// after\nif err != nil {\n    if errors.Is(err, context.Canceled) {\n        return err // don't log user-initiated cancellation as failure\n    }\n    slog.Warn(\"tts: synthesis failed, falling back to text reply\", \"error\", err)\n    return fmt.Errorf(\"synthesize: %w\", err)\n}","handlingStrategy":"try-catch","validationCode":"// before enabling TTS, verify provider works\nif e.tts != nil && e.tts.TTS != nil {\n    if _, _, err := e.tts.TTS.Synthesize(ctx, \"ping\", opts); err != nil {\n        slog.Warn(\"tts provider unavailable, disabling TTS\", \"error\", err)\n    }\n}","typeGuard":"func ttsAvailable(e *Engine) bool {\n    return e != nil && e.tts != nil && e.tts.TTS != nil\n}","tryCatchPattern":"if err != nil {\n    var ctxErr error\n    switch {\n    case errors.Is(err, context.Canceled), errors.Is(err, context.DeadlineExceeded):\n        return err // expected during shutdown\n    default:\n        slog.Error(\"tts synthesize failed\", \"cause\", errors.Unwrap(err))\n        return fmt.Errorf(\"synthesize: %w\", err)\n    }\n    _ = ctxErr\n}","preventionTips":["Smoke-test the TTS provider at startup and degrade to text replies on failure","Keep TTS credentials and endpoint in config.toml under version-controlled review","Set a sane Synthesize timeout shorter than the user-facing reply deadline","Monitor provider quotas before they exhaust"],"tags":["tts","audio","error-wrapping","go"],"backgroundTag":"upstream-api-error","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"}