zeroclaw-labs/zeroclaw · error

Composio execute failed on v3 ({v3_error_summary}){prime_suf

Error message

Composio execute failed on v3 ({v3_error_summary}){prime_suffix}{}

What it means

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.

Source

Thrown at crates/zeroclaw-tools/src/composio.rs:273

        };

        let prime_suffix = prime_error
            .as_deref()
            .map(|msg| format!(" ({msg})"))
            .unwrap_or_default();

        if text.is_some() {
            anyhow::bail!(
                "Composio v3 NLP execute failed on candidates ({v3_error_summary}){prime_suffix}{}",
                build_connected_account_hint(
                    app_hint.as_deref(),
                    normalized_entity_id.as_deref(),
                    resolved_account_ref.as_deref(),
                )
            );
        }

        anyhow::bail!(
            "Composio execute failed on v3 ({v3_error_summary}){prime_suffix}{}",
            build_connected_account_hint(
                app_hint.as_deref(),
                normalized_entity_id.as_deref(),
                resolved_account_ref.as_deref(),
            )
        );
    }

    fn build_v3_slug_candidates(&self, action_name: &str) -> Vec<String> {
        let mut candidates = Vec::new();
        let mut push_candidate = |candidate: String| {
            if !candidate.is_empty() && !candidates.contains(&candidate) {
                candidates.push(candidate);
            }
        };

        if let Some(hit) = self.lookup_cached_action_slug(action_name) {

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Read the appended 'Expected input parameters' hint (or the per-candidate errors) and resend params with exactly those key names, filling every required field.
  2. Pass the exact slug as tool_slug taken from action='list' output instead of an alias.
  3. 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.
  4. Make sure entity_id matches the user_id the account was connected under.
  5. Fix the API key (401) or back off (429).

Example fix

// before: guessed param names
let args = json!({
    "action": "execute",
    "app": "gmail",
    "action_name": "gmail_send_email",
    "params": {"to": "a@b.c", "body_text": "hi"}
});

// after: exact slug + schema key names from action='list' [params: ...] hint
let args = json!({
    "action": "execute",
    "tool_slug": "gmail-send-email",
    "params": {"recipient": "a@b.c", "body": "hi"},
    "connected_account_id": "<from list_accounts>"
});
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate param keys against the tool's published schema before executing
let actions = tool.list_actions(Some("gmail")).await?;
let action = actions
    .iter()
    .find(|a| a.name == "gmail-send-email")
    .ok_or_else(|| anyhow::anyhow!("unknown action"))?;
if let Some(schema) = action.input_parameters.as_ref() {
    let props = schema.get("properties").and_then(|v| v.as_object()).unwrap_or_default();
    for key in params.as_object().unwrap().keys() {
        if !props.contains_key(key) {
            anyhow::bail!(
                "param '{key}' not in schema; valid: {:?}",
                props.keys().collect::<Vec<_>>()
            );
        }
    }
}

Try / catch

match tool.execute(args).await {
    Ok(r) if r.success => { /* success */ }
    Ok(r) => {
        let e = r.error.unwrap_or_default();
        if e.contains("execute failed on v3") {
            // parse the 'Expected input parameters:' hint and self-correct params;
            // if the trailing Hint mentions list_accounts, resolve the account instead
        }
    }
    Err(e) => { /* transport error */ }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/5768437c71204217. Report an issue: GitHub.