{"record":{"id":"68e02cd79de819c1","repo":"tinyhumansai/openhuman","slug":"managed-cloud-tools-are-disabled-because-your-open","errorCode":null,"errorMessage":"Managed cloud tools are disabled because your OpenHuman AI credits are exhausted. Add credits or route the task to user-supplied providers.","messagePattern":"Managed cloud tools are disabled because your OpenHuman AI credits are exhausted\\. Add credits or route the task to user-supplied providers\\.","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/openhuman/integrations/client.rs","lineNumber":386,"sourceCode":"            .expect(\"failed to build integration download HTTP client\");\n\n        Self {\n            backend_url,\n            auth_token,\n            budget_config,\n            sdk,\n            download_client,\n            pricing: tokio::sync::OnceCell::new(),\n        }\n    }\n\n    async fn ensure_budget_available(&self, path: &str) -> anyhow::Result<()> {\n        if !managed_budget_applies_to_path(path) {\n            return Ok(());\n        }\n        if let Some(config) = &self.budget_config {\n            if crate::openhuman::hosted::team::managed_tool_budget_exhausted(config).await {\n                anyhow::bail!(\n                    \"Managed cloud tools are disabled because your OpenHuman AI credits are exhausted. Add credits or route the task to user-supplied providers.\"\n                );\n            }\n        }\n        Ok(())\n    }\n\n    /// POST JSON to a backend endpoint and parse the response `data` field.\n    pub async fn post<T: serde::de::DeserializeOwned>(\n        &self,\n        path: &str,\n        body: &serde_json::Value,\n    ) -> anyhow::Result<T> {\n        self.request_json(reqwest::Method::POST, path, Some(body))\n            .await\n    }\n\n    /// GET from a backend endpoint and parse the response `data` field.","sourceCodeStart":368,"sourceCodeEnd":404,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/src/openhuman/integrations/client.rs#L368-L404","documentation":"Thrown by IntegrationClient::ensure_budget_available before any managed call whose path starts with /agent-integrations/ (everything except /agent-integrations/pricing). It probes the hosted team usage endpoint via managed_tool_budget_exhausted (result cached for a TTL); when the OpenHuman AI credit pool that funds managed cloud tools is drained, the client fails fast instead of making the backend round-trip. A failed probe is NOT cached and lets the call through, so seeing this error means the probe succeeded and reported exhausted.","triggerScenarios":"Any IntegrationClient.post/get/raw_delete to a /agent-integrations/* path (e.g. POST /agent-integrations/composio/execute, the Composio trigger endpoints) while team.get_usage reports the managed tool budget exhausted. Because the exhausted flag is TTL-cached, the error keeps firing for a short window even immediately after a top-up.","commonSituations":"Free/trial credits used up mid-task; a team budget cap reached while an agent loop hammers managed Composio tools; a stale cached exhausted flag from just before credits were refilled; shared team account drained by another member.","solutions":["Add OpenHuman AI credits (or raise the team budget cap) in the hosted account/billing UI, then retry after the budget probe cache TTL expires","Route the task to user-supplied provider keys (BYOK) so the call bypasses the managed budget path entirely","Confirm exhaustion first via the team usage RPC (hosted::team::get_usage) to distinguish real exhaustion from other failures"],"exampleFix":"// before — every managed call fails while credits are out\nclient.post(\"/agent-integrations/composio/execute\", &body).await?;\n\n// after — probe the budget gate first, fall back to a user-supplied provider\nif let Some(cfg) = &budget_config {\n    if openhuman::hosted::team::managed_tool_budget_exhausted(cfg).await {\n        return run_with_user_supplied_provider(&body).await;\n    }\n}\nclient.post(\"/agent-integrations/composio/execute\", &body).await?;","handlingStrategy":"fallback","validationCode":"// Probe the budget gate before a managed call (same TTL-cached check the client uses)\nuse openhuman::hosted::team::managed_tool_budget_exhausted;\n\nasync fn managed_call_allowed(budget_config: &Option<Config>) -> bool {\n    match budget_config {\n        Some(cfg) => !managed_tool_budget_exhausted(cfg).await,\n        None => true, // no budget configured -> gate does not apply\n    }\n}","typeGuard":null,"tryCatchPattern":"match client.post(\"/agent-integrations/composio/execute\", &body).await {\n    Ok(resp) => Ok(resp),\n    Err(err) if err.to_string().contains(\"credits are exhausted\") => {\n        // Budget state, not a bug: switch provider instead of retrying\n        run_with_user_supplied_provider(&body).await\n    }\n    Err(err) => Err(err),\n}","preventionTips":["Surface remaining managed credits in the UI before they hit zero instead of letting agents discover exhaustion mid-task","Cache the exhausted probe result yourself and short-circuit managed calls during the window instead of paying the error path","Wire agent tool routing to fall back to user-supplied keys when the budget error appears — retrying the same managed path cannot succeed until credits are added"],"tags":["billing","credits","integrations","managed-tools","quota"],"backgroundTag":"quota-exhausted","analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}