chenhg5/cc-connect · error

synthesize

Error message

synthesize: %w

What it means

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.

Solutions

  1. Check the wrapped cause with errors.Unwrap(err) or %v of the error to see the provider-specific failure
  2. Verify TTS provider credentials/endpoint in the [tts] section of config.toml
  3. Test the TTS provider directly (curl / SDK sample) to rule out outage or quota limits
  4. Check network egress/proxy settings on the host running cc-connect
  5. Retry — transient network or rate-limit failures resolve on their own

Example fix

// before
if err != nil {
    return fmt.Errorf("synthesize: %w", err)
}
// after
if err != nil {
    if errors.Is(err, context.Canceled) {
        return err // don't log user-initiated cancellation as failure
    }
    slog.Warn("tts: synthesis failed, falling back to text reply", "error", err)
    return fmt.Errorf("synthesize: %w", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// before enabling TTS, verify provider works
if e.tts != nil && e.tts.TTS != nil {
    if _, _, err := e.tts.TTS.Synthesize(ctx, "ping", opts); err != nil {
        slog.Warn("tts provider unavailable, disabling TTS", "error", err)
    }
}

Type guard

func ttsAvailable(e *Engine) bool {
    return e != nil && e.tts != nil && e.tts.TTS != nil
}

Try / catch

if err != nil {
    var ctxErr error
    switch {
    case errors.Is(err, context.Canceled), errors.Is(err, context.DeadlineExceeded):
        return err // expected during shutdown
    default:
        slog.Error("tts synthesize failed", "cause", errors.Unwrap(err))
        return fmt.Errorf("synthesize: %w", err)
    }
    _ = ctxErr
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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

Appendix: source

Thrown at core/engine.go:15889

	if e.tts.TTS == nil {
		return fmt.Errorf("tts provider is not configured")
	}
	if e.tts.MaxTextLen > 0 && utf8.RuneCountInString(text) > e.tts.MaxTextLen {
		return fmt.Errorf("text exceeds max_text_len (%d > %d)", utf8.RuneCountInString(text), e.tts.MaxTextLen)
	}
	as, ok := p.(AudioSender)
	if !ok {
		return fmt.Errorf("platform %s does not support audio sending", p.Name())
	}
	slog.Info("tts: starting synthesis", "voice", e.tts.Voice, "speed", e.tts.Speed, "text_len", len(text))
	opts := TTSSynthesisOpts{
		Voice:        e.tts.Voice,
		LanguageType: e.tts.LanguageType,
		Speed:        e.tts.Speed,
	}
	audioData, format, err := e.tts.TTS.Synthesize(e.ctx, StripMarkdown(text), opts)
	if err != nil {
		return fmt.Errorf("synthesize: %w", err)
	}
	slog.Info("tts: synthesis successful", "format", format, "audio_size", len(audioData))
	if err := as.SendAudio(e.ctx, replyCtx, audioData, format); err != nil {
		return fmt.Errorf("send audio: %w", err)
	}
	slog.Info("tts: audio sent successfully", "platform", p.Name())
	return nil
}

// ──────────────────────────────────────────────────────────────
// Bot-to-bot relay
// ──────────────────────────────────────────────────────────────

type platformNameOnly struct {
	name string
}

func (p platformNameOnly) Name() string                           { return p.name }

View on GitHub (pinned to 4000b2338a)