{"record":{"id":"5768437c71204217","repo":"zeroclaw-labs/zeroclaw","slug":"composio-execute-failed-on-v3-v3-error-summary","errorCode":null,"errorMessage":"Composio execute failed on v3 ({v3_error_summary}){prime_suffix}{}","messagePattern":"Composio execute failed on v3 \\((.+?)\\)(.+?)(.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-tools/src/composio.rs","lineNumber":273,"sourceCode":"        };\n\n        let prime_suffix = prime_error\n            .as_deref()\n            .map(|msg| format!(\" ({msg})\"))\n            .unwrap_or_default();\n\n        if text.is_some() {\n            anyhow::bail!(\n                \"Composio v3 NLP execute failed on candidates ({v3_error_summary}){prime_suffix}{}\",\n                build_connected_account_hint(\n                    app_hint.as_deref(),\n                    normalized_entity_id.as_deref(),\n                    resolved_account_ref.as_deref(),\n                )\n            );\n        }\n\n        anyhow::bail!(\n            \"Composio execute failed on v3 ({v3_error_summary}){prime_suffix}{}\",\n            build_connected_account_hint(\n                app_hint.as_deref(),\n                normalized_entity_id.as_deref(),\n                resolved_account_ref.as_deref(),\n            )\n        );\n    }\n\n    fn build_v3_slug_candidates(&self, action_name: &str) -> Vec<String> {\n        let mut candidates = Vec::new();\n        let mut push_candidate = |candidate: String| {\n            if !candidate.is_empty() && !candidates.contains(&candidate) {\n                candidates.push(candidate);\n            }\n        };\n\n        if let Some(hit) = self.lookup_cached_action_slug(action_name) {","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-tools/src/composio.rs#L255-L291","documentation":"Structured-parameter counterpart of the NLP failure: execute_action tried every slug candidate with the caller's params JSON against POST /tools/execute/{slug} and all attempts failed. v3_error_summary lists 'slug: error' per candidate, and a trailing Hint suggests action='list_accounts' when no connected_account_id was passed or auto-resolved. On this path the outer execute() additionally fetches the tool schema via get_tool_schema and appends an 'Expected input parameters:' block to ToolResult.error, so parameter-name mismatches are easy to spot.","triggerScenarios":"action='execute' with params whose keys do not match the tool's input schema (LLM-guessed names); missing required parameters; no connected account or an expired OAuth token for the entity; every slug candidate 404s (typos, renamed tools, stale cache); 401/429/5xx from Composio.","commonSituations":"Agent invents parameter names instead of using action='list' output; account connected under a different entity_id than the configured default; Composio renamed a tool so both the cached slug and spelling variants miss; free-tier rate limits hit mid-task.","solutions":["Read the appended 'Expected input parameters' hint (or the per-candidate errors) and resend params with exactly those key names, filling every required field.","Pass the exact slug as tool_slug taken from action='list' output instead of an alias.","Run action='list_accounts' (app + entity_id): pass connected_account_id if an account exists, or run action='connect' first if the list is empty.","Make sure entity_id matches the user_id the account was connected under.","Fix the API key (401) or back off (429)."],"exampleFix":"// before: guessed param names\nlet args = json!({\n    \"action\": \"execute\",\n    \"app\": \"gmail\",\n    \"action_name\": \"gmail_send_email\",\n    \"params\": {\"to\": \"a@b.c\", \"body_text\": \"hi\"}\n});\n\n// after: exact slug + schema key names from action='list' [params: ...] hint\nlet args = json!({\n    \"action\": \"execute\",\n    \"tool_slug\": \"gmail-send-email\",\n    \"params\": {\"recipient\": \"a@b.c\", \"body\": \"hi\"},\n    \"connected_account_id\": \"<from list_accounts>\"\n});","handlingStrategy":"try-catch","validationCode":"// Validate param keys against the tool's published schema before executing\nlet actions = tool.list_actions(Some(\"gmail\")).await?;\nlet action = actions\n    .iter()\n    .find(|a| a.name == \"gmail-send-email\")\n    .ok_or_else(|| anyhow::anyhow!(\"unknown action\"))?;\nif let Some(schema) = action.input_parameters.as_ref() {\n    let props = schema.get(\"properties\").and_then(|v| v.as_object()).unwrap_or_default();\n    for key in params.as_object().unwrap().keys() {\n        if !props.contains_key(key) {\n            anyhow::bail!(\n                \"param '{key}' not in schema; valid: {:?}\",\n                props.keys().collect::<Vec<_>>()\n            );\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"match tool.execute(args).await {\n    Ok(r) if r.success => { /* success */ }\n    Ok(r) => {\n        let e = r.error.unwrap_or_default();\n        if e.contains(\"execute failed on v3\") {\n            // parse the 'Expected input parameters:' hint and self-correct params;\n            // if the trailing Hint mentions list_accounts, resolve the account instead\n        }\n    }\n    Err(e) => { /* transport error */ }\n}","preventionTips":["Always source param names from action='list' [params: ...] output","Pin tool_slug instead of sending aliases","Pass connected_account_id once resolved and cache it per user/app","Log the full ToolResult.error — it already embeds the schema hint"],"tags":["composio","rust","params","oauth","execute","schema"],"backgroundTag":"action-execution-failed","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}