{"record":{"id":"0b53fdd57fa3c454","repo":"tonhowtf/omniget","slug":"voicestudio-http","errorCode":null,"errorMessage":"VoiceStudio HTTP {}: {}","messagePattern":"VoiceStudio HTTP (.+?): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/core/tools/voicestudio.rs","lineNumber":194,"sourceCode":"/// Abre o app do VoiceStudio (que sobe o backend).\npub async fn launch() -> anyhow::Result<()> {\n    let app = find_app().ok_or_else(|| anyhow!(\"VoiceStudio nao encontrado\"))?;\n    if cfg!(target_os = \"macos\") {\n        crate::core::process::command(\"open\")\n            .arg(&app)\n            .output()\n            .await?;\n    } else {\n        crate::core::process::command(&app).spawn()?;\n    }\n    Ok(())\n}\n\nasync fn wav_from(resp: reqwest::Response, output: &Path) -> anyhow::Result<PathBuf> {\n    let status = resp.status();\n    if !status.is_success() {\n        let text = resp.text().await.unwrap_or_default();\n        return Err(anyhow!(\n            \"VoiceStudio HTTP {}: {}\",\n            status.as_u16(),\n            text.chars().take(300).collect::<String>()\n        ));\n    }\n    let bytes = resp.bytes().await?;\n    if bytes.len() < 100 {\n        return Err(anyhow!(\"resposta vazia\"));\n    }\n    if let Some(parent) = output.parent() {\n        std::fs::create_dir_all(parent)?;\n    }\n    std::fs::write(output, &bytes)?;\n    Ok(output.to_path_buf())\n}\n\nfn stamp() -> String {\n    chrono::Local::now().format(\"%Y%m%d-%H%M%S\").to_string()","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/tools/voicestudio.rs#L176-L212","documentation":"wav_from checks the HTTP status of a VoiceStudio TTS/voice-API response and, on any non-success status, aborts by formatting the status code plus up to 300 chars of the response body into an anyhow error. It surfaces server-side rejections (auth, rate limits, bad payloads) from the VoiceStudio backend to the caller.","triggerScenarios":"Any HTTP call made by clone_speak, design_speak, or isolate whose response status is not 2xx: invalid API key, expired token, oversized or malformed audio, unknown profile_id, or server-side 5xx during synthesis/isolation.","commonSituations":"Expired or wrong VoiceStudio credentials, uploading a voice sample that exceeds server limits, referencing a deleted voice profile, or the VoiceStudio service being overloaded and returning 500/503.","solutions":["Read the status and body text in the error message to identify the server-side cause (401/403 = credentials, 404 = bad profile/endpoint, 413 = file too large, 5xx = server issue).","Verify the VoiceStudio API key/base URL configuration used to build the reqwest client.","For 4xx, fix the request: confirm profile_id exists, audio format/size is accepted, and parameters are valid.","For 5xx or 429, retry with backoff and check VoiceStudio service health.","Log the full body (not just 300 chars) in server logs if the truncated message is insufficient."],"exampleFix":"// before\nlet text = resp.text().await.unwrap_or_default();\nreturn Err(anyhow!(\"VoiceStudio HTTP {}: {}\", status.as_u16(), text.chars().take(300).collect::<String>()));\n// after\nlet status_code = status.as_u16();\nlet text = resp.text().await.unwrap_or_default();\nif status_code == 401 || status_code == 403 {\n    anyhow::bail!(\"VoiceStudio authentication failed (HTTP {}): check your API key\", status_code);\n}\nanyhow::bail!(\"VoiceStudio HTTP {}: {}\", status_code, text.chars().take(300).collect::<String>());","handlingStrategy":"try-catch","validationCode":"// Pre-check connectivity/auth cheaply before long synthesis jobs\nlet health = reqwest::get(format!(\"{}/health\", base_url)).await;\nif let Ok(r) = &health {\n    if !r.status().is_success() {\n        return Err(format!(\"VoiceStudio unavailable: HTTP {}\", r.status()));\n    }\n}","typeGuard":null,"tryCatchPattern":"match clone_speak(&opts).await {\n    Err(e) if e.to_string().contains(\"VoiceStudio HTTP\") => {\n        let msg = e.to_string();\n        if msg.contains(\"401\") || msg.contains(\"403\") {\n            // refresh credentials and retry once\n        } else if msg.contains(\"429\") || msg.contains(\"5\") {\n            // backoff and retry\n        } else {\n            // surface msg (status + body) to the user\n        }\n    }\n    Err(e) => eprintln!(\"unexpected: {e}\"),\n    Ok(path) => println!(\"saved to {}\", path.display()),\n}","preventionTips":["Validate the API key and base URL with a cheap health/auth call before long jobs.","Check audio file size/format limits client-side before upload.","Implement retry with exponential backoff for 429/5xx responses.","Verify profile ids against the profiles list API before use."],"tags":["http","network","api","rust"],"backgroundTag":"http-error-response","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}