tinyhumansai/openhuman · error · anyhow::Error

composio.execute_tool: tool slug must not be empty

Error message

composio.execute_tool: tool slug must not be empty

What it means

ComposioClient::execute_tool rejects a tool slug that is empty after trimming, before the egress descriptor is built and the POST /agent-integrations/composio/execute request is sent. The guard runs before privacy disclosure (emit_external_transfer) so a blank slug never counts as an external transfer.

Source

Thrown at src/openhuman/integrations/composio/client.rs:192

        } else {
            format!("/agent-integrations/composio/tools?{}", params.join("&"))
        };
        tracing::debug!(path = %path, "[composio] list_tools");
        self.inner.get::<ComposioToolsResponse>(&path).await
    }

    // ── Execute ─────────────────────────────────────────────────────

    /// `POST /agent-integrations/composio/execute` — run a Composio
    /// action and return the provider result + cost.
    pub async fn execute_tool(
        &self,
        tool: &str,
        arguments: Option<serde_json::Value>,
    ) -> Result<ComposioExecuteResponse> {
        let tool = tool.trim();
        if tool.is_empty() {
            anyhow::bail!("composio.execute_tool: tool slug must not be empty");
        }
        // Egress spine (privacy epic S2, #4436): a Composio tool call ships the
        // (already-normalized) arguments to the third-party provider — disclose
        // the transfer before the round-trip. S4 will add an approval arm here.
        let egress = crate::openhuman::security::egress::EgressDescriptor::composio(tool);
        // Local-only enforcement (privacy epic S7, #4441): refuse the external
        // tool call under LocalOnly BEFORE disclosing or sending it.
        crate::openhuman::security::egress::enforce_egress(&egress)?;
        crate::openhuman::security::egress::emit_external_transfer(egress);
        // PR #1827 routes all execute-side argument normalization
        // (including the bare-date → RFC 3339 fix #1802 brought to
        // `normalize_calendar_query_args` on `main`) through the
        // centralized `prepare_execute_arguments` helper. The helper
        // covers the same calendar query case and is the shared entry
        // point for `composio_execute`, per-action tools, and direct-
        // mode dispatch.
        let arguments = super::execute_prepare::prepare_execute_arguments(tool, arguments)
            .map_err(anyhow::Error::msg)?;

View on GitHub (pinned to 7491200858)

Solutions

  1. Fix the producer of the slug — an empty slug is a caller bug, not a Composio failure
  2. Validate and trim the slug where the tool call is parsed, and re-prompt or skip on blank
  3. Cross-check slug values against the tool list from list_tools so typos and empties are both caught early

Example fix

// before
let resp = client.execute_tool(action.as_str(), Some(args)).await?;

// after
let action = action.trim();
if action.is_empty() {
    anyhow::bail!("tool call arrived with an empty action slug");
}
let resp = client.execute_tool(action, Some(args)).await?;
Defensive patterns

Strategy: validation

Validate before calling

let tool = tool.trim();
if tool.is_empty() {
    anyhow::bail!("tool slug is required for composio execution");
}
let resp = client.execute_tool(tool, arguments).await?;

Type guard

fn is_non_empty_slug(s: &str) -> bool {
    !s.trim().is_empty()
}

Try / catch

match client.execute_tool(tool, arguments).await {
    Ok(resp) => Ok(resp),
    Err(err) if err.to_string().contains("tool slug must not be empty") => {
        re_prompt_agent_for_action().await
    }
    Err(err) => Err(err),
}

Prevention

When it happens

Trigger: Calling execute_tool("", arguments) or with a whitespace-only slug — typically an agent/LLM tool invocation whose action name is missing while arguments are present.

Common situations: LLM emits a tool call with a blank function name; slug built by string concatenation where one part was empty; tool name read from a renamed/missing config or tool-definition key.

Related errors


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