tinyhumansai/openhuman · error · anyhow::Error
Composio action listing failed on v3 ({v3_err}) and v2 fallb
Error message
Composio action listing failed on v3 ({v3_err}) and v2 fallback ({v2_err}) What it means
ComposioTool::list_actions tries GET v3 /tools first (limit=200, toolkit_versions=latest) and falls back to legacy GET v2 /actions; this error means BOTH attempts failed. The message embeds both underlying errors (v3 first), so the pair tells you whether the cause is shared (auth, network, outage) or v3-specific breakage that the v2 fallback could not paper over.
Source
Thrown at src/openhuman/integrations/composio/tools/direct.rs:208
return Ok(());
}
ensure_https(url)
}
/// List available Composio apps/actions for the authenticated user.
///
/// Uses v3 endpoint first and falls back to v2 for compatibility.
pub async fn list_actions(
&self,
app_name: Option<&str>,
) -> anyhow::Result<Vec<ComposioAction>> {
match self.list_actions_v3(app_name).await {
Ok(items) => Ok(items),
Err(v3_err) => {
let v2 = self.list_actions_v2(app_name).await;
match v2 {
Ok(items) => Ok(items),
Err(v2_err) => anyhow::bail!(
"Composio action listing failed on v3 ({v3_err}) and v2 fallback ({v2_err})"
),
}
}
}
}
async fn list_actions_v3(&self, app_name: Option<&str>) -> anyhow::Result<Vec<ComposioAction>> {
let url = format!("{}/tools", self.base_v3);
let mut req = self.client().get(&url).header("x-api-key", &self.api_key);
// #3932: pin toolkit_versions=latest. Composio v3 otherwise defaults to
// the 00000000_00 snapshot, which lists zero tools for any toolkit
// published after it (Outlook and every other post-launch toolkit).
req = req.query(&[("limit", "200"), ("toolkit_versions", "latest")]);
if let Some(app) = app_name.map(str::trim).filter(|app| !app.is_empty()) {
req = req.query(&[("toolkits", app), ("toolkit_slug", app)]);
}View on GitHub (pinned to 7491200858)
Solutions
- Read both embedded errors: identical 401s point at the API key, both timing out points at network/egress, differing 4xx bodies point at malformed query params
- Verify the key: curl -H 'x-api-key: <key>' 'https://backend.composio.dev/api/v3/tools?limit=1'
- For transient 5xx/429, retry with backoff — the built-in v3-to-v2 fallback is already exhausted
- If v3 consistently fails while v2 works (or vice versa), capture both messages and check the Composio changelog/status page
Defensive patterns
Strategy: retry
Try / catch
match tool.list_actions(Some(app)).await {
Ok(actions) => Ok(actions),
Err(e) => {
let msg = format!("{e:#}");
if msg.contains("401") {
Err(e) // auth is broken on both versions — fix the key, retrying is pointless
} else if msg.contains("429") || msg.contains("5") /* transient shapes */ {
backoff::retry_after(Duration::from_secs(30), 3).await?; // then re-invoke once
Err(e)
} else { Err(e) }
}
} Prevention
- Log both embedded errors (v3 and v2) — their relationship identifies auth vs network vs v3-specific breakage
- Verify the API key works with a one-off curl before wiring an environment
- Cache the last successful action list so listing outages degrade gracefully
- Add jittered backoff around 429s instead of immediate re-listing
When it happens
Trigger: Invalid API key (401 on both endpoints); no network egress to backend.composio.dev; a Composio outage returning 5xx on both API versions; 429 rate limiting on both. Concretely: list_actions(Some("github")) with a revoked key yields 'v3 (Composio v3 API error: 401 ...) and v2 fallback (Composio v2 API error: 401 ...)'.
Common situations: Expired/revoked key during app usage; laptop offline or DNS failure; Composio incident; CI environment with blocked egress running integration tests.
Related errors
- Composio v2 API error: {err}
- Composio v3 API error: {err}
- Composio v3 list_tool_schemas: {err}
- Composio execute failed on v3 ({v3_err}) and v2 fallback ({v
- Composio connect failed on v3 ({v3_err}) and v2 fallback ({v
AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17).
Data as JSON: /api/errors/39b23918fa7fe3c9.
Report an issue: GitHub.