{"record":{"id":"da98d971beb2f943","repo":"tonhowtf/omniget","slug":"ai-keys","errorCode":null,"errorMessage":"{}","messagePattern":"\\{\\}","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/core/tools/ai_keys.rs","lineNumber":491,"sourceCode":"            )\n        }\n        \"newapi\" => {\n            if entry.access_token.is_empty() {\n                return Err(anyhow!(\n                    \"informe o token de acesso e o ID de usuario do painel\"\n                ));\n            }\n            let j: serde_json::Value = c\n                .get(format!(\"{}/api/user/self\", site_of(&entry.base_url)))\n                .bearer_auth(&entry.access_token)\n                .header(\"New-Api-User\", &entry.user_id)\n                .send()\n                .await?\n                .error_for_status()?\n                .json()\n                .await?;\n            if j[\"success\"].as_bool() == Some(false) {\n                return Err(anyhow!(\n                    \"{}\",\n                    j[\"message\"].as_str().unwrap_or(\"painel recusou\")\n                ));\n            }\n            let d = &j[\"data\"];\n            let quota = d[\"quota\"].as_f64().unwrap_or(0.0) / 500_000.0;\n            let used = d[\"used_quota\"].as_f64().unwrap_or(0.0) / 500_000.0;\n            format!(\"{} ({} usados)\", usd(quota), usd(used))\n        }\n        _ => return Err(anyhow!(\"este provedor nao expoe saldo pela API\")),\n    };\n    update(id, |e| e.balance = Some(text))\n}\n\n// ── Exportar ───────────────────────────────────────────────────────────\n\nfn is_openai_compatible(kind: &str) -> bool {\n    !matches!(kind, \"anthropic\" | \"gemini\")","sourceCodeStart":473,"sourceCodeEnd":509,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/tools/ai_keys.rs#L473-L509","documentation":"After fetching the newapi panel's /api/user/self endpoint, balance() checks the JSON's success flag; if it is explicitly false it throws the panel's own message (or the fallback 'painel recusou'). This surfaces panel-side rejections such as invalid token, wrong user ID, or suspended accounts, wrapped via anyhow!(\"{}\", ...).","triggerScenarios":"Calling balance(id) for a 'newapi' entry where the panel responds {success:false,message:...}: expired access token, mismatched user ID, banned/deactivated account, or insufficient permission for the API route.","commonSituations":"Panel sessions/tokens expire and users keep querying balance with stale tokens; user ID changed after re-registration; panel admin disabled API access.","solutions":["Read the embedded panel message (j.message) for the specific rejection reason.","Regenerate the access token in the panel and update the key entry.","Confirm the user ID stored in the entry matches the token's account.","Check account standing with the panel administrator (suspension/quota bans cause refusals)."],"exampleFix":"// before\n{ success: false, message: \"无权进行此操作，未登录或未登录已过期\" }\n// after: refresh token in panel, then\nentry.access_token = \"<newly generated token>\";","handlingStrategy":"try-catch","validationCode":"// pre-validate panel credentials via the same endpoint\nlet j: serde_json::Value = client\n    .get(format!(\"{base}/api/user/self\"))\n    .bearer_auth(&access_token)\n    .send().await?.json().await?;\nif j[\"success\"].as_bool() == Some(false) {\n    eprintln!(\"panel refused: {}\", j[\"message\"].as_str().unwrap_or(\"?\"));\n}","typeGuard":"fn panel_accepted(j: &serde_json::Value) -> bool { j[\"success\"].as_bool() != Some(false) }","tryCatchPattern":"match balance(id) {\n    Ok(text) => show(text),\n    Err(e) if e.to_string().contains(\"painel recusou\") || /* panel message */ true => {\n        if is_auth_related(&e.to_string()) { prompt_token_refresh(); } else { show_error(e); }\n    }\n    Err(e) => show_error(e),\n}","preventionTips":["Regenerate panel access tokens when sessions expire","Keep user ID in sync with the token's account","Surface the panel's own message to the user instead of generic errors","Check account standing with panel admins for repeated refusals"],"tags":["api","authentication","panel"],"backgroundTag":"api-error-response","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}