{"record":{"id":"d656a887a5ce563b","repo":"tinyhumansai/openhuman","slug":"composio-v2-connect-failed-err","errorCode":null,"errorMessage":"Composio v2 connect failed: {err}","messagePattern":"Composio v2 connect failed: (.+?)","errorType":"http","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/openhuman/integrations/composio/tools/direct.rs","lineNumber":603,"sourceCode":"    ) -> anyhow::Result<String> {\n        let url = format!(\"{}/connectedAccounts\", self.base_v2);\n\n        let body = json!({\n            \"integrationId\": app_name,\n            \"entityId\": entity_id,\n        });\n\n        let resp = self\n            .client()\n            .post(&url)\n            .header(\"x-api-key\", &self.api_key)\n            .json(&body)\n            .send()\n            .await?;\n\n        if !resp.status().is_success() {\n            let err = response_error(resp).await;\n            anyhow::bail!(\"Composio v2 connect failed: {err}\");\n        }\n\n        let result: serde_json::Value = resp\n            .json()\n            .await\n            .context(\"Failed to decode Composio v2 connect response\")?;\n        extract_redirect_url(&result)\n            .ok_or_else(|| anyhow::anyhow!(\"No redirect URL in Composio v2 response\"))\n    }\n\n    /// List the user's connected accounts on Composio v3.\n    ///\n    /// GET `https://backend.composio.dev/api/v3/connected_accounts` with\n    /// `x-api-key: <user_key>`. Returns the raw item list; reshaping\n    /// into [`super::super::super::composio::types::ComposioConnection`]\n    /// happens at the call site in `composio/client.rs::direct_list_connections`.\n    ///\n    /// The v3 envelope is `{ items: [{ id, status, toolkit, created_at, ... }] }`.","sourceCodeStart":585,"sourceCodeEnd":621,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/src/openhuman/integrations/composio/tools/direct.rs#L585-L621","documentation":"Non-2xx from the legacy v2 POST connect fallback (get_connection_url_v2). Reached only when v3 connect already failed and an app name was available. response_error folds status + body into {err}; on success a redirect URL is extracted, so a missing redirect is a separate error.","triggerScenarios":"Unknown appName in the v2 registry (404/400); app enabled only in v3; 401; 429/5xx. Typically the second half of the combined connect failure message.","commonSituations":"Newer toolkits absent from the v2 catalogue; appName passed with wrong casing or a display name; auth/network problems hitting both versions.","solutions":["Diagnose the v3 error first — v2 failing with a similar 4xx usually confirms the app/slug problem","Use Composio's canonical app key for v2 if you must target it","401: fix the API key; 429/5xx: retry with backoff"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match tool.get_connection_url(app, None, &entity).await {\n    Ok(url) => ui.open(url),\n    Err(e) => {\n        // v3 already failed and v2 (appName route) failed too — report the pair verbatim\n        tracing::warn!(\"[composio] connect failed both APIs: {e:#}\");\n        ui.show_connect_error(&format!(\"{e:#}\"));\n    }\n}","preventionTips":["Prefer fixing the v3 connect path — v2 appName coverage is narrower and shrinking","Use Composio's canonical v2 app keys if you must target v2 directly","Do not offer connect buttons for toolkits absent from the workspace's registry"],"tags":["composio","connect","http-error","v2","oauth","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"}