{"record":{"id":"fa708cb839d76ad9","repo":"sipeed/picoclaw","slug":"api-error-status-d-s","errorCode":null,"errorMessage":"API error (status %d): %s","messagePattern":"API error \\(status (.+?)\\): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/audio/tts/mimo_tts.go","lineNumber":134,"sourceCode":"\t\treturn nil, fmt.Errorf(\"failed to create request: %w\", err)\n\t}\n\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\treq.Header.Set(\"Api-Key\", t.apiKey)\n\n\tresp, err := t.httpClient.Do(req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to send request: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read response: %w\", err)\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"API error (status %d): %s\", resp.StatusCode, string(body))\n\t}\n\n\tvar payload struct {\n\t\tChoices []struct {\n\t\t\tMessage struct {\n\t\t\t\tAudio struct {\n\t\t\t\t\tData string `json:\"data\"`\n\t\t\t\t} `json:\"audio\"`\n\t\t\t} `json:\"message\"`\n\t\t} `json:\"choices\"`\n\t}\n\n\terr = json.Unmarshal(body, &payload)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to decode response: %w\", err)\n\t}\n\n\tif len(payload.Choices) == 0 || payload.Choices[0].Message.Audio.Data == \"\" {","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/audio/tts/mimo_tts.go#L116-L152","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\np := tts.NewMimoTTSProvider(``, ``, ``, ``) // empty key -> 401 later\n// after\np := tts.NewMimoTTSProvider(os.Getenv(`MIMO_API_KEY`), ``, `mimo-v2-tts`, ``)","handlingStrategy":"try-catch","validationCode":"if strings.TrimSpace(apiKey) == `` {\n    return errors.New(`mimo tts requires an api_key on the model config entry`)\n}\nif model != `` && !strings.Contains(strings.ToLower(model), `tts`) {\n    return errors.New(`mimo tts expects a TTS model such as mimo-v2-tts`)\n}","typeGuard":"func apiStatus(err error) (int, bool) {\n    s := err.Error()\n    i := strings.Index(s, `API error (status `)\n    if i < 0 {\n        return 0, false\n    }\n    rest := s[i+len(`API error (status `):]\n    j := strings.IndexByte(rest, ')')\n    if j < 0 {\n        return 0, false\n    }\n    n, cerr := strconv.Atoi(rest[:j])\n    return n, cerr == nil\n}","tryCatchPattern":"if code, ok := apiStatus(err); ok {\n    switch {\n    case code == 401:\n        // fix the api_key; the header used is Api-Key, not Bearer\n    case code == 429:\n        // rate limited: exponential backoff, then retry\n    case code >= 500:\n        // provider incident: retry later\n    default:\n        // inspect the embedded body for invalid model/voice parameters\n    }\n}","preventionTips":["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"],"tags":["api","auth","rate-limit","tts","mimo"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}