tinyhumansai/openhuman · error · anyhow::Error
Composio execute failed on v3 ({v3_err}) and v2 fallback ({v
Error message
Composio execute failed on v3 ({v3_err}) and v2 fallback ({v2_err}) What it means
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.
Source
Thrown at src/openhuman/integrations/composio/tools/direct.rs:399
// (`gmail-send-email`) and posted to the wrong `/tools/{slug}/execute`
// path, so every direct-mode execute 404'd (issue #3219). Pass the
// action slug through verbatim (trimmed only); the v2 fallback already
// used the same untransformed name.
let action_slug = action_name.trim();
match self
.execute_action_v3(
action_slug,
params.clone(),
entity_id,
connected_account_ref,
)
.await
{
Ok(result) => Ok(result),
Err(v3_err) => match self.execute_action_v2(action_name, params, entity_id).await {
Ok(result) => Ok(result),
Err(v2_err) => anyhow::bail!(
"Composio execute failed on v3 ({v3_err}) and v2 fallback ({v2_err})"
),
},
}
}
fn build_execute_action_v3_request(
action_slug: &str,
params: serde_json::Value,
entity_id: Option<&str>,
connected_account_ref: Option<&str>,
) -> (String, serde_json::Value) {
// POST /api/v3/tools/execute/{ACTION_SLUG} — the action slug stays
// UPPERCASE_SNAKE (see `execute_action`). Path is `/tools/execute/{slug}`,
// NOT `/tools/{slug}/execute` (issue #3219).
let url = format!("{COMPOSIO_API_BASE_V3}/tools/execute/{action_slug}");
let account_ref = connected_account_ref.and_then(|candidate| {
let trimmed_candidate = candidate.trim();View on GitHub (pinned to 7491200858)
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)
Defensive patterns
Strategy: try-catch
Try / catch
match tool.execute_action(slug, params, entity, account).await {
Ok(result) => Ok(result),
Err(e) => {
let msg = format!("{e:#}");
// NEVER auto-retry: the action may have partially or fully executed.
if msg.contains("connected") || msg.contains("401") {
ui.prompt_reconnect(app_for(slug));
}
Err(e)
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Composio v3 action execution failed: {err}
- Composio v2 action execution failed: {err}
- composio.execute_tool: tool slug must not be empty
- composio.execute_tool_once: tool slug must not be empty
- composio direct_execute: tool slug must not be empty
AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17).
Data as JSON: /api/errors/d12cd55f1837436b.
Report an issue: GitHub.