{"record":{"id":"80f45df4bc72646c","repo":"tinyhumansai/openhuman","slug":"composio-v2-api-error-err","errorCode":null,"errorMessage":"Composio v2 API error: {err}","messagePattern":"Composio v2 API error: (.+?)","errorType":"http","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/openhuman/integrations/composio/tools/direct.rs","lineNumber":256,"sourceCode":"        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)\n            .header(\"x-api-key\", &self.api_key)\n            .send()\n            .await?;\n\n        if !resp.status().is_success() {\n            let err = response_error(resp).await;\n            anyhow::bail!(\"Composio v2 API error: {err}\");\n        }\n\n        let body: ComposioActionsResponse = resp\n            .json()\n            .await\n            .context(\"Failed to decode Composio v2 actions response\")?;\n        Ok(body.items)\n    }\n\n    /// Build the query-parameter pairs for the Composio v3 `GET /tools`\n    /// listing used by [`Self::list_tool_schemas_v3`].\n    ///\n    /// `toolkits` is sent as a single comma-joined `toolkits=` param (the\n    /// legacy plural the v3 backend tolerates; cf. `list_actions_v3` which\n    /// sends both the plural and `toolkit_slug` singular forms). `tags` is\n    /// encoded as **repeated** `tags=` params (`tags=a&tags=b`) — the shape\n    /// Composio v3 `/tools` documents for tag filtering (\"can be specified\n    /// multiple times\"), NOT the comma-joined form the backend proxy uses.","sourceCodeStart":238,"sourceCodeEnd":274,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/src/openhuman/integrations/composio/tools/direct.rs#L238-L274","documentation":"Non-2xx from the legacy v2 GET /actions endpoint (list_actions_v2), optionally filtered with ?appNames=<app>. response_error folds status + body into {err}. This endpoint is only reached as the compatibility fallback after v3 /tools already failed, so seeing it alone means a caller used the v2 path directly.","triggerScenarios":"Same failure classes as v3 but on v2: 401 invalid key, 400 unknown appNames value, 429, 5xx. Typically the second half of the combined 'failed on v3 ... and v2 fallback ...' message from list_actions.","commonSituations":"v2-only behavior differences: appNames expects the Composio v2 app key (e.g. 'GITHUB' vs 'github' depending on toolkit), or the toolkit only exists in v3 so v2 answers 404/400.","solutions":["Treat this as confirmation the failure is not v3-specific — check auth and network first (401/timeouts appearing in both messages)","If v2 4xx differs from v3 4xx, the v2 appNames value may be wrong for the legacy API — prefer fixing the v3 failure and letting the fallback stay unused","429/5xx: retry with backoff","For toolkits published after the v2 freeze, expect v2 to have no data — rely on the v3 path"],"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        if msg.contains(\"v2 fallback\") {\n            // both versions failed — classify from the embedded pair before any retry\n            classify_and_report(msg);\n        }\n    }\n}","preventionTips":["Remember v2 /actions is only a fallback — do not build logic that depends on v2-specific fields","Newer toolkits may not exist in v2; treat v2 404 with a healthy v3 as expected, not an error","Log both halves of combined v3+v2 failures to spot version-specific drift"],"tags":["composio","api","http-error","v2","actions","legacy-fallback"],"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"}