{"record":{"id":"c93e746d70fea0df","repo":"tonhowtf/omniget","slug":"tradu-o-por-ia-falhou","errorCode":null,"errorMessage":"tradução por IA falhou: {}","messagePattern":"tradução por IA falhou: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/core/tools/srt_translate.rs","lineNumber":119,"sourceCode":"        &opts.source_lang,\n        &opts.target_lang,\n        &opts.context,\n        prev,\n        next,\n    );\n    let mut last_err = String::new();\n    for _ in 0..2 {\n        match crate::core::ai::chat(&system, &user).await {\n            Ok(text) => {\n                if let Some(parsed) = parse_llm_json(&text, lines.len()) {\n                    return Ok(parsed);\n                }\n                last_err = \"resposta fora do formato JSON\".to_string();\n            }\n            Err(e) => last_err = e,\n        }\n    }\n    Err(anyhow!(\"tradução por IA falhou: {}\", last_err))\n}\n\nasync fn translate_batch_libre(\n    lines: &[&str],\n    base_url: &str,\n    api_key: &str,\n    opts: &TranslateOptions,\n) -> anyhow::Result<Vec<Option<String>>> {\n    let client = super::client()?;\n    let url = format!(\"{}/translate\", base_url.trim_end_matches('/'));\n    let mut body = serde_json::json!({\n        \"q\": lines,\n        \"source\": if opts.source_lang.is_empty() { \"auto\" } else { opts.source_lang.as_str() },\n        \"target\": opts.target_lang,\n        \"format\": \"text\",\n    });\n    if !api_key.is_empty() {\n        body[\"api_key\"] = serde_json::Value::String(api_key.to_string());","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/tools/srt_translate.rs#L101-L137","documentation":"translate_batch_llm tries to translate a batch of SRT cue lines via an LLM; if every attempt fails (network errors, JSON responses outside the expected shape, etc.), it exhausts its retries and returns this error with the last failure reason interpolated. It is an aggregate 'all attempts failed' error, not a single-shot failure.","triggerScenarios":"Calling translate_cues with an LLM backend when the endpoint is unreachable, the API key is rejected, the model returns non-JSON or malformed-JSON output ('resposta fora do formato JSON'), or every retry attempt errors out.","commonSituations":"Wrong/missing API key or base URL for the LLM provider; the model ignoring the JSON output instruction and returning prose; rate limits or timeouts on all retries; model name typo; free-tier quota exhausted.","solutions":["Read the interpolated last_err: it names the reason of the final attempt (network error vs bad JSON).","Verify the LLM base URL and API key are correct and the account has quota.","Use a model that reliably follows JSON output instructions, or enable the provider's JSON/response-format mode.","If LLM translation keeps failing, switch to the LibreTranslate backend (translate_batch_libre path).","Increase timeout/batch-size settings if large batches are being truncated into invalid JSON."],"exampleFix":"// before\nllm_base_url: \"http://localhost:8080\".into(), api_key: \"\".into() // server not running / unauthorized\n// after\nllm_base_url: \"https://api.openai.com/v1\".into(), api_key: std::env::var(\"OPENAI_API_KEY\")?,\n// plus response_format: { \"type\": \"json_object\" } in the request","handlingStrategy":"fallback","validationCode":"fn llm_config_ok(base_url: &str, api_key: &str) -> bool {\n    !base_url.trim().is_empty() && !api_key.trim().is_empty() && base_url.starts_with(\"http\")\n}","typeGuard":null,"tryCatchPattern":"match translate_cues(cues, Llm { .. }).await {\n    Err(e) if e.to_string().starts_with(\"tradução por IA falhou\") => translate_cues(cues, LibreTranslate { .. }).await?,\n    other => other,\n}","preventionTips":["Configure a valid LLM base URL and API key; verify with a smoke-test request at startup.","Request JSON output mode from the model to avoid 'resposta fora do formato JSON'.","Keep batches small so responses are not truncated into invalid JSON.","Configure LibreTranslate as a fallback backend."],"tags":["translation","llm","srt","rust","api"],"backgroundTag":"upstream-api-error","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"}