{"record":{"id":"d12cd55f1837436b","repo":"tinyhumansai/openhuman","slug":"composio-execute-failed-on-v3-v3-err-and-v2-fa","errorCode":null,"errorMessage":"Composio execute failed on v3 ({v3_err}) and v2 fallback ({v2_err})","messagePattern":"Composio execute failed on v3 \\((.+?)\\) and v2 fallback \\((.+?)\\)","errorType":"http","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/openhuman/integrations/composio/tools/direct.rs","lineNumber":399,"sourceCode":"        // (`gmail-send-email`) and posted to the wrong `/tools/{slug}/execute`\n        // path, so every direct-mode execute 404'd (issue #3219). Pass the\n        // action slug through verbatim (trimmed only); the v2 fallback already\n        // used the same untransformed name.\n        let action_slug = action_name.trim();\n\n        match self\n            .execute_action_v3(\n                action_slug,\n                params.clone(),\n                entity_id,\n                connected_account_ref,\n            )\n            .await\n        {\n            Ok(result) => Ok(result),\n            Err(v3_err) => match self.execute_action_v2(action_name, params, entity_id).await {\n                Ok(result) => Ok(result),\n                Err(v2_err) => anyhow::bail!(\n                    \"Composio execute failed on v3 ({v3_err}) and v2 fallback ({v2_err})\"\n                ),\n            },\n        }\n    }\n\n    fn build_execute_action_v3_request(\n        action_slug: &str,\n        params: serde_json::Value,\n        entity_id: Option<&str>,\n        connected_account_ref: Option<&str>,\n    ) -> (String, serde_json::Value) {\n        // POST /api/v3/tools/execute/{ACTION_SLUG} — the action slug stays\n        // UPPERCASE_SNAKE (see `execute_action`). Path is `/tools/execute/{slug}`,\n        // NOT `/tools/{slug}/execute` (issue #3219).\n        let url = format!(\"{COMPOSIO_API_BASE_V3}/tools/execute/{action_slug}\");\n        let account_ref = connected_account_ref.and_then(|candidate| {\n            let trimmed_candidate = candidate.trim();","sourceCodeStart":381,"sourceCodeEnd":417,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/src/openhuman/integrations/composio/tools/direct.rs#L381-L417","documentation":"execute_action tries v3 POST execute (carrying connected_account_ref and entity) then falls back to v2 POST /actions/execute; this error means both attempts failed. v3_err is usually the informative half (param validation, expired connected account), with v2 re-expressing the same root cause through the legacy API. Because this is an action execution, callers must not blindly retry — the action may have side effects.","triggerScenarios":"Executing a Composio action (e.g. creating an issue) when: the connected account for the app is expired/disconnected (4xx on v3), action params fail validation, the action slug is unknown/wrong-case (v3 uses UPPER_SNAKE like GITHUB_CREATE_ISSUE), the API key is invalid (401), or the network is down.","commonSituations":"User disconnected the app in Composio but the agent still tries to act; agent invents an action name or passes free-text params; workspace switch invalidated old connected accounts.","solutions":["Read v3_err first — param-validation and connected-account errors surface there with the most detail","Verify the connected account still exists: GET v3 /connected_accounts; if expired, re-run the connect flow to get a fresh OAuth grant","Check the action slug format and casing, and that params match the action's schema","Do NOT auto-retry: executions are not idempotent — only retry after confirming the action did not run (e.g. a send-phase network failure)"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match tool.execute_action(slug, params, entity, account).await {\n    Ok(result) => Ok(result),\n    Err(e) => {\n        let msg = format!(\"{e:#}\");\n        // NEVER auto-retry: the action may have partially or fully executed.\n        if msg.contains(\"connected\") || msg.contains(\"401\") {\n            ui.prompt_reconnect(app_for(slug));\n        }\n        Err(e)\n    }\n}","preventionTips":["Never blind-retry executions — verify the action did not run (query the target system) before re-invoking","Pre-check the connected account exists via GET v3 /connected_accounts before executing on a stale connection","Validate params against the v3 tool schema before sending to catch 4xx locally","Use exact UPPER_SNAKE action slugs from the schema listing, never guessed names"],"tags":["composio","execute","action","fallback-exhausted","side-effects","connected-account"],"backgroundTag":"upstream-api-fallback-exhausted","analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}