sipeed/picoclaw · error
tts synthesize failed: %w
Error message
tts synthesize failed: %w
What it means
Returned by tts.SynthesizeAndStore wrapping any failure from provider.Synthesize — the top-level TTS error. The chain carries the concrete cause: mimo/openai transport errors ('failed to send request'), provider rejections ('API error (status ...)'), JSON decode failures, or missing audio data. Unwrap the chain to see which layer failed.
Source
Thrown at pkg/audio/tts/tts.go:113
channel string,
chatID string,
) (string, error) {
if provider == nil {
return "", fmt.Errorf("tts provider is not configured")
}
if store == nil {
return "", fmt.Errorf("media store not configured")
}
if channel == "" || chatID == "" {
return "", fmt.Errorf("no target channel/chat available")
}
if strings.TrimSpace(text) == "" {
return "", fmt.Errorf("text is required")
}
stream, err := provider.Synthesize(ctx, text)
if err != nil {
return "", fmt.Errorf("tts synthesize failed: %w", err)
}
defer stream.Close()
err = os.MkdirAll(media.TempDir(), 0o700)
if err != nil {
return "", fmt.Errorf("failed to create media temp dir: %w", err)
}
fileExt := ".ogg"
contentType := "audio/ogg"
if provider.Name() == "mimo-tts" {
fileExt = ".mp3"
contentType = "audio/mpeg"
}
if metaProvider, ok := stream.(ttsAudioMetaProvider); ok {
if ext, ct := metaProvider.AudioFileMeta(); ext != "" && ct != "" {
fileExt = ext
contentType = ctView on GitHub (pinned to 49183d7e8d)
Solutions
- Unwrap the error chain (errors.As / errors.Is) to classify the root cause before acting
- Fix per root cause: 401 → key config, 429/5xx/network → backoff and retry, decode → provider drift
- Configure a fallback TTSProvider so voice output survives one provider failing
- Retry idempotent synthesis with backoff for transient causes
Example fix
// before
ref, err := tts.SynthesizeAndStore(ctx, p, s, text, name, ch, id)
// after
ref, err := tts.SynthesizeAndStore(ctx, p, s, text, name, ch, id)
if err != nil && strings.HasPrefix(err.Error(), `tts synthesize failed`) && fallback != nil {
ref, err = tts.SynthesizeAndStore(ctx, fallback, s, text, name, ch, id)
} Defensive patterns
Strategy: fallback
Type guard
func isTTSWrap(err error) bool {
return err != nil && strings.HasPrefix(err.Error(), `tts synthesize failed`)
} Try / catch
if err != nil && isTTSWrap(err) {
// unwrap: errors.Unwrap(err) yields the provider-level cause (transport / API status / decode)
if fallback != nil {
ref, err = tts.SynthesizeAndStore(ctx, fallback, store, text, name, channel, chatID)
}
} Prevention
- Unwrap and classify the cause before choosing retry vs fallback
- Keep a second TTSProvider configured for failover
- Retry only transient causes (network, 429, 5xx) with backoff
- Alert on repeated synthesis failures — usually key expiry or provider incidents
When it happens
Trigger: Any provider-level failure: network outage to api.openai.com or api.xiaomimimo.com, 401/429 from the provider, schema or base64 encoding drift, a model that returns no audio.
Common situations: Production voice pipelines; provider incidents; expired API keys; rate limits during bulk synthesis.
Related errors
- Failed to load config
- Failed to fetch config
- error fetching models: %w
- transcription request failed: %w
- failed to send request: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/890dbc2c93742cdc.
Report an issue: GitHub.