sipeed/picoclaw · error
API error (status %d): %s
Error message
API error (status %d): %s
What it means
Returned by MimoTTSProvider.Synthesize when the MiMo chat/completions endpoint answers a non-200 status; the status code and raw body are embedded in the message. This is the provider rejecting the request: 401 for a bad Api-Key header (the code uses 'Api-Key', not Bearer), 400 for an unknown model or voice, 429 for quota/rate limits, 5xx for provider outages.
Source
Thrown at pkg/audio/tts/mimo_tts.go:134
return nil, fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Api-Key", t.apiKey)
resp, err := t.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to send request: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response: %w", err)
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(body))
}
var payload struct {
Choices []struct {
Message struct {
Audio struct {
Data string `json:"data"`
} `json:"audio"`
} `json:"message"`
} `json:"choices"`
}
err = json.Unmarshal(body, &payload)
if err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
if len(payload.Choices) == 0 || payload.Choices[0].Message.Audio.Data == "" {View on GitHub (pinned to 49183d7e8d)
Solutions
- Read the embedded status and body: 401 → fix the api_key, 400 → fix model/voice, 429 → back off, 5xx → wait and retry
- Confirm the model config entry has api_key set and the model is mimo-v2-tts (or another TTS-capable name)
- Retry 429/5xx with exponential backoff and jitter
- If the body names an invalid parameter, simplify the request parameters
Example fix
// before p := tts.NewMimoTTSProvider(``, ``, ``, ``) // empty key -> 401 later // after p := tts.NewMimoTTSProvider(os.Getenv(`MIMO_API_KEY`), ``, `mimo-v2-tts`, ``)
Defensive patterns
Strategy: try-catch
Validate before calling
if strings.TrimSpace(apiKey) == `` {
return errors.New(`mimo tts requires an api_key on the model config entry`)
}
if model != `` && !strings.Contains(strings.ToLower(model), `tts`) {
return errors.New(`mimo tts expects a TTS model such as mimo-v2-tts`)
} Type guard
func apiStatus(err error) (int, bool) {
s := err.Error()
i := strings.Index(s, `API error (status `)
if i < 0 {
return 0, false
}
rest := s[i+len(`API error (status `):]
j := strings.IndexByte(rest, ')')
if j < 0 {
return 0, false
}
n, cerr := strconv.Atoi(rest[:j])
return n, cerr == nil
} Try / catch
if code, ok := apiStatus(err); ok {
switch {
case code == 401:
// fix the api_key; the header used is Api-Key, not Bearer
case code == 429:
// rate limited: exponential backoff, then retry
case code >= 500:
// provider incident: retry later
default:
// inspect the embedded body for invalid model/voice parameters
}
} Prevention
- Set api_key on the mimo model entry — an empty key guarantees 401
- Pin the model to mimo-v2-tts and the voice to default_zh
- Rate-limit bulk synthesis to avoid 429s
When it happens
Trigger: Empty or wrong apiKey passed to NewMimoTTSProvider; model name set to a non-TTS model; voice or format values the endpoint rejects (defaults: model mimo-v2-tts, voice default_zh, format mp3); exhausted quota returning 429.
Common situations: api_key missing from the model config so APIKey() returns ''; using text model names like 'mimo-v2' instead of 'mimo-v2-tts'; free-tier rate limits during bulk synthesis.
Related errors
- HTTP %d: %s
- failed to decode response: %w
- invalid TTS response: missing audio data
- ElevenLabs API error (status %d): %s
- failed to send request: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/fa708cb839d76ad9.
Report an issue: GitHub.