{"record":{"id":"72c7231a60fab457","repo":"tinyhumansai/openhuman","slug":"composio-v3-api-error-err","errorCode":null,"errorMessage":"Composio v3 API error: {err}","messagePattern":"Composio v3 API error: (.+?)","errorType":"http","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/openhuman/integrations/composio/tools/direct.rs","lineNumber":231,"sourceCode":"        }\n    }\n\n    async fn list_actions_v3(&self, app_name: Option<&str>) -> anyhow::Result<Vec<ComposioAction>> {\n        let url = format!(\"{}/tools\", self.base_v3);\n        let mut req = self.client().get(&url).header(\"x-api-key\", &self.api_key);\n\n        // #3932: pin toolkit_versions=latest. Composio v3 otherwise defaults to\n        // the 00000000_00 snapshot, which lists zero tools for any toolkit\n        // published after it (Outlook and every other post-launch toolkit).\n        req = req.query(&[(\"limit\", \"200\"), (\"toolkit_versions\", \"latest\")]);\n        if let Some(app) = app_name.map(str::trim).filter(|app| !app.is_empty()) {\n            req = req.query(&[(\"toolkits\", app), (\"toolkit_slug\", app)]);\n        }\n\n        let resp = req.send().await?;\n        if !resp.status().is_success() {\n            let err = response_error(resp).await;\n            anyhow::bail!(\"Composio v3 API error: {err}\");\n        }\n\n        let body: ComposioToolsResponse = resp\n            .json()\n            .await\n            .context(\"Failed to decode Composio v3 tools response\")?;\n        Ok(map_v3_tools_to_actions(body.items))\n    }\n\n    async fn list_actions_v2(&self, app_name: Option<&str>) -> anyhow::Result<Vec<ComposioAction>> {\n        let mut url = format!(\"{}/actions\", self.base_v2);\n        if let Some(app) = app_name {\n            url = format!(\"{url}?appNames={app}\");\n        }\n\n        let resp = self\n            .client()\n            .get(&url)","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/src/openhuman/integrations/composio/tools/direct.rs#L213-L249","documentation":"Non-2xx from the v3 GET /tools listing (list_actions_v3). The request pins limit=200 and toolkit_versions=latest (without that pin Composio serves the 00000000_00 snapshot and lists zero tools for post-launch toolkits), and adds toolkits/toolkit_slug filters when an app name is given. response_error(resp) folds the HTTP status and response body into {err}, which this bail re-raises.","triggerScenarios":"401 invalid API key; 400 for a malformed/unknown toolkit slug in the toolkits or toolkit_slug query param; 429 rate limit; 5xx from Composio. Surfaced directly when list_actions_v3 is the failing path, or embedded inside the combined v3+v2 error when the fallback also fails.","commonSituations":"Bad key or typo'd app name ('gh' vs 'github'); polling too aggressively and hitting rate limits during a Composio incident; a toolkit slug renamed upstream.","solutions":["Inspect {err} — it contains the HTTP status and Composio's response body","401: fix the API key (Connections > Composio)","429: back off and retry; the client does not throttle for you","400: correct the app/toolkit slug spelling used for toolkits/toolkit_slug","5xx: transient — retry with backoff and check Composio status"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match tool.list_actions(Some(app)).await {\n    Ok(actions) => { /* ... */ }\n    Err(e) => {\n        let msg = format!(\"{e:#}\");\n        match () {\n            _ if msg.contains(\"401\") => ui.prompt_reenter_composio_key(),\n            _ if msg.contains(\"429\") || msg.contains(\"50\") => scheduler.retry_with_backoff(),\n            _ => tracing::warn!(\"[composio] tools listing failed: {msg}\"),\n        }\n    }\n}","preventionTips":["Read the status inside {err} before deciding to retry — 401 and 400 are not retryable","Keep toolkit_versions=latest in any custom query or post-launch toolkits list zero tools","Rate-limit listing calls; Composio answers 429 quickly when polled hard"],"tags":["composio","api","http-error","v3","tools-listing"],"backgroundTag":"upstream-api-error-status","analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}