{"record":{"id":"f406939996a42284","repo":"Billionmail/BillionMail","slug":"cartesia-tts-api-error-d-s","errorCode":null,"errorMessage":"cartesia TTS API error %d: %s","messagePattern":"cartesia TTS API error (.+?): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/video_gen/voice.go","lineNumber":195,"sourceCode":"\t\tOutputFormat: DefaultTTSOutputFormat(),\n\t\tLanguage:     \"en\",\n\t}\n\n\thttpReq, err := BuildTTSRequest(cfg, req)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\thttpReq = httpReq.WithContext(ctx)\n\n\tresp, err := cfg.doHTTP(httpReq)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"cartesia TTS API call: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tbody, _ := io.ReadAll(resp.Body)\n\t\treturn \"\", fmt.Errorf(\"cartesia TTS API error %d: %s\", resp.StatusCode, string(body))\n\t}\n\n\toutPath := filepath.Join(cfg.OutputDir, filename)\n\tf, err := os.Create(outPath)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"create output file: %w\", err)\n\t}\n\tdefer f.Close()\n\n\tif _, err := io.Copy(f, resp.Body); err != nil {\n\t\treturn \"\", fmt.Errorf(\"write audio data: %w\", err)\n\t}\n\n\treturn outPath, nil\n}\n","sourceCodeStart":177,"sourceCodeEnd":211,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/video_gen/voice.go#L177-L211","documentation":"When the Cartesia /tts/bytes endpoint responds with a status other than 200, TextToSpeech reads the response body and returns 'cartesia TTS API error <code>: <body>'. Like the clone counterpart, the embedded body is Cartesia's own error message explaining the rejection.","triggerScenarios":"Invalid or missing voice_id (404/400), invalid API key (401), unsupported model_id 'sonic-2' or output format combo (400), rate limits (429), Cartesia outage (5xx), or requesting a voice the account does not own.","commonSituations":"Stale voiceID from a deleted cloned voice, wrong sample_rate/encoding combination for the model, exhausted quota, deprecated model name after a Cartesia model update.","solutions":["Read the embedded Cartesia body — it identifies invalid voice, model, or auth.","Verify voiceID exists and belongs to the account (list voices or re-clone).","Validate the TTSOutputFormat combo (wav/44100/pcm_f32le) is supported by sonic-2.","Handle 429 with backoff; check quota in the Cartesia dashboard.","Upgrade or pin Cartesia-Version if the model_id was rejected."],"exampleFix":"// before\nif resp.StatusCode != http.StatusOK {\n    body, _ := io.ReadAll(resp.Body)\n    return \"\", fmt.Errorf(\"cartesia TTS API error %d: %s\", resp.StatusCode, string(body))\n}\n// after\nif voiceID == \"\" {\n    return \"\", errors.New(\"voice id is empty\")\n}\nif resp.StatusCode == http.StatusTooManyRequests || resp.StatusCode >= 500 {\n    return \"\", retryable(fmt.Errorf(\"cartesia TTS API error %d\", resp.StatusCode))\n}\nif resp.StatusCode != http.StatusOK {\n    body, _ := io.ReadAll(resp.Body)\n    return \"\", fmt.Errorf(\"cartesia TTS API error %d: %s\", resp.StatusCode, string(body))\n}","handlingStrategy":"retry","validationCode":"// validate inputs before the TTS call\nif voiceID == \"\" {\n    return errors.New(\"voiceID is required\")\n}\nif strings.TrimSpace(transcript) == \"\" {\n    return errors.New(\"transcript is required\")\n}\nif cfg.APIKey == \"\" {\n    return errors.New(\"CARTESIA_API_KEY is not set\")\n}\n// optionally confirm the voice exists:\n// GET /voices/ and check voiceID is present before synthesizing","typeGuard":null,"tryCatchPattern":"path, err := video_gen.TextToSpeech(ctx, cfg, voiceID, transcript, filename)\nif err != nil {\n    if strings.Contains(err.Error(), \"cartesia TTS API error 429\") ||\n        strings.Contains(err.Error(), \"cartesia TTS API error 5\") {\n        // retry with exponential backoff\n    }\n    if strings.Contains(err.Error(), \"cartesia TTS API error 4\") {\n        // 400/401/404: fix voiceID/key/format, do not retry\n    }\n    return err\n}","preventionTips":["Cache and validate voiceIDs; handle deleted voices gracefully","Verify the output format combo is supported by the chosen model_id","Track Cartesia quota and alert before hitting 429","Subscribe to Cartesia changelogs for model deprecations"],"tags":["http","api","cartesia","tts"],"backgroundTag":"upstream-api-non-2xx","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}