tinyhumansai/openhuman · error · anyhow::Error
Composio v2 API error: {err}
Error message
Composio v2 API error: {err} What it means
Non-2xx from the legacy v2 GET /actions endpoint (list_actions_v2), optionally filtered with ?appNames=<app>. response_error folds status + body into {err}. This endpoint is only reached as the compatibility fallback after v3 /tools already failed, so seeing it alone means a caller used the v2 path directly.
Source
Thrown at src/openhuman/integrations/composio/tools/direct.rs:256
Ok(map_v3_tools_to_actions(body.items))
}
async fn list_actions_v2(&self, app_name: Option<&str>) -> anyhow::Result<Vec<ComposioAction>> {
let mut url = format!("{}/actions", self.base_v2);
if let Some(app) = app_name {
url = format!("{url}?appNames={app}");
}
let resp = self
.client()
.get(&url)
.header("x-api-key", &self.api_key)
.send()
.await?;
if !resp.status().is_success() {
let err = response_error(resp).await;
anyhow::bail!("Composio v2 API error: {err}");
}
let body: ComposioActionsResponse = resp
.json()
.await
.context("Failed to decode Composio v2 actions response")?;
Ok(body.items)
}
/// Build the query-parameter pairs for the Composio v3 `GET /tools`
/// listing used by [`Self::list_tool_schemas_v3`].
///
/// `toolkits` is sent as a single comma-joined `toolkits=` param (the
/// legacy plural the v3 backend tolerates; cf. `list_actions_v3` which
/// sends both the plural and `toolkit_slug` singular forms). `tags` is
/// encoded as **repeated** `tags=` params (`tags=a&tags=b`) — the shape
/// Composio v3 `/tools` documents for tag filtering ("can be specified
/// multiple times"), NOT the comma-joined form the backend proxy uses.View on GitHub (pinned to 7491200858)
Solutions
- Treat this as confirmation the failure is not v3-specific — check auth and network first (401/timeouts appearing in both messages)
- If v2 4xx differs from v3 4xx, the v2 appNames value may be wrong for the legacy API — prefer fixing the v3 failure and letting the fallback stay unused
- 429/5xx: retry with backoff
- For toolkits published after the v2 freeze, expect v2 to have no data — rely on the v3 path
Defensive patterns
Strategy: try-catch
Try / catch
match tool.list_actions(Some(app)).await {
Ok(actions) => { /* ... */ }
Err(e) => {
let msg = format!("{e:#}");
if msg.contains("v2 fallback") {
// both versions failed — classify from the embedded pair before any retry
classify_and_report(msg);
}
}
} Prevention
- Remember v2 /actions is only a fallback — do not build logic that depends on v2-specific fields
- Newer toolkits may not exist in v2; treat v2 404 with a healthy v3 as expected, not an error
- Log both halves of combined v3+v2 failures to spot version-specific drift
When it happens
Trigger: Same failure classes as v3 but on v2: 401 invalid key, 400 unknown appNames value, 429, 5xx. Typically the second half of the combined 'failed on v3 ... and v2 fallback ...' message from list_actions.
Common situations: v2-only behavior differences: appNames expects the Composio v2 app key (e.g. 'GITHUB' vs 'github' depending on toolkit), or the toolkit only exists in v3 so v2 answers 404/400.
Related errors
- Composio v2 action execution failed: {err}
- Composio v2 connect failed: {err}
- Composio action listing failed on v3 ({v3_err}) and v2 fallb
- Composio v3 API error: {err}
- Composio v3 list_tool_schemas: {err}
AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17).
Data as JSON: /api/errors/80f45df4bc72646c.
Report an issue: GitHub.