zeroclaw-labs/zeroclaw · warning

Composio v3 tool schema lookup failed for '{slug}': {err}

Error message

Composio v3 tool schema lookup failed for '{slug}': {err}

What it means

get_tool_schema fetches GET {v3}/tools/{slug}?version=latest (the slug is normalized to lowercase with hyphens) to retrieve a tool's input schema, and the call returned non-2xx — most often 404 for a name that is not a real v3 tool slug. Within Tool::execute this call is best-effort: after an execute failure it is invoked with .ok() to append an 'Expected input parameters' hint, so the error itself is swallowed there; its visible symptom is a missing schema-hint block in the execute error output.

Source

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

    /// Fetch full metadata for a single tool by slug, including input/output parameter schemas.
    /// Calls `GET /api/v3/tools/{tool_slug}` which returns the detailed schema
    /// the LLM needs to construct correct `params` for `execute`.
    async fn get_tool_schema(&self, tool_slug: &str) -> anyhow::Result<serde_json::Value> {
        let slug = normalize_tool_slug(tool_slug);
        let url = format!("{COMPOSIO_API_BASE_V3}/tools/{slug}");
        ensure_https(&url)?;

        let resp = self
            .client()
            .get(&url)
            .header("x-api-key", &self.api_key)
            .query(&[("version", COMPOSIO_TOOL_VERSION_LATEST)])
            .send()
            .await?;

        if !resp.status().is_success() {
            let err = response_error(resp).await;
            anyhow::bail!("Composio v3 tool schema lookup failed for '{slug}': {err}");
        }

        let body: serde_json::Value = resp
            .json()
            .await
            .context("Failed to decode Composio v3 tool schema response")?;
        Ok(body)
    }

    async fn resolve_auth_config_id(&self, app_name: &str) -> anyhow::Result<String> {
        let url = format!("{COMPOSIO_API_BASE_V3}/auth_configs");

        let resp = self
            .client()
            .get(&url)
            .header("x-api-key", &self.api_key)
            .query(&[
                ("toolkit_slug", app_name),

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. If you relied on the hint, get authoritative parameters from action='list' with the app filter (its [params: ...] lines) instead.
  2. Pass the exact v3 slug so the schema lookup URL is valid.
  3. On 401 fix the API key; on 429 back off.
  4. Treat as non-fatal: the primary execute error and its per-candidate details are already in ToolResult.error.
Defensive patterns

Strategy: retry

Validate before calling

// Only rely on schema hints for slugs that exist in the catalog
let actions = tool.list_actions(Some(app)).await?;
let known: Vec<&str> = actions.iter().map(|a| a.name.as_str()).collect();
if !known.contains(&slug.as_str()) {
    anyhow::bail!("'{slug}' is not a catalog slug; schema lookup would 404");
}

Prevention

When it happens

Trigger: An execute failed and the fallback schema lookup used the raw action_name (a legacy alias such as GITHUB_LIST_REPOS or a free-text name) whose normalized form is not a v3 slug (404); invalid API key (401); rate limit (429); backend 5xx.

Common situations: Aliases that differ from catalog slugs; tools renamed upstream by Composio; the schema fetch hitting rate limits right after a failed execute inside tight agent loops.

Related errors


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