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

  1. 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
  2. Verify the key: curl -H 'x-api-key: <key>' 'https://backend.composio.dev/api/v3/tools?limit=1'
  3. For transient 5xx/429, retry with backoff — the built-in v3-to-v2 fallback is already exhausted
  4. 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

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


AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17). Data as JSON: /api/errors/39b23918fa7fe3c9. Report an issue: GitHub.