tinyhumansai/openhuman · error · anyhow::Error

composio direct_execute: tool slug must not be empty

Error message

composio direct_execute: tool slug must not be empty

What it means

direct_execute invokes Composio's v3 /tools/{slug}/execute endpoint in direct mode; it rejects a tool slug that is empty after trimming before building params or the URL. It is the direct-mode counterpart of execute_tool's guard, so blank slugs fail identically on both the proxied and direct paths.

Source

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

/// the v3 `/tools/{slug}/execute` envelope into [`ComposioExecuteResponse`]
/// so the caller doesn't branch on mode for the
/// `ComposioActionExecuted` event-bus payload or the
/// markdown-vs-JSON-body preference.
///
/// Direct mode runs without the backend's billing margin, so `cost_usd`
/// is reported as `0.0`. The backend's `markdownFormatted` field is
/// likewise specific to the backend-proxied path and remains `None` for
/// direct callers, which fall back to the raw JSON envelope.
pub async fn direct_execute(
    direct: &Arc<crate::openhuman::tools::ComposioTool>,
    tool: &str,
    arguments: Option<serde_json::Value>,
    entity_id: &str,
    connection_id: Option<&str>,
) -> anyhow::Result<ComposioExecuteResponse> {
    let tool = tool.trim();
    if tool.is_empty() {
        anyhow::bail!("composio direct_execute: tool slug must not be empty");
    }
    let params = arguments.unwrap_or_else(|| serde_json::Value::Object(Default::default()));
    let entity_id = entity_id.trim();
    let entity_id_opt = (!entity_id.is_empty()).then_some(entity_id);
    let conn_id = connection_id.map(str::trim).filter(|s| !s.is_empty());
    tracing::debug!(
        tool = %tool,
        has_entity = entity_id_opt.is_some(),
        connection_id = ?conn_id,
        "[composio-direct] execute: invoking v3 /tools/{{slug}}/execute"
    );
    let raw = direct
        .execute_action(tool, params, entity_id_opt, conn_id)
        .await?;
    // v3 surfaces `successful` + `data` + `error` at the top level. If
    // none are present, treat the call as success so callers see the
    // raw payload instead of an empty error envelope.
    let successful = raw

View on GitHub (pinned to 7491200858)

Solutions

  1. Validate and trim the slug in the caller before invoking direct mode, and re-prompt or skip when blank
  2. Filter blank entries when building tool lists for batch execution
  3. Share one slug-validation helper across the proxied and direct call sites so both paths guard identically

Example fix

// before
let resp = direct_execute(&tool, action.as_str(), Some(args), entity, conn).await?;

// after
let action = action.trim();
if action.is_empty() {
    anyhow::bail!("direct composio execute received an empty tool slug");
}
let resp = direct_execute(&tool, action, Some(args), entity, conn).await?;
Defensive patterns

Strategy: validation

Validate before calling

let tool = tool.trim();
if tool.is_empty() {
    anyhow::bail!("tool slug is required for direct composio execution");
}
let resp = direct_execute(&direct_tool, tool, arguments, entity_id, connection_id).await?;

Type guard

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

Try / catch

match direct_execute(&tool, slug, args, entity, conn).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 direct_execute(&direct_tool, "", arguments, entity_id, connection_id) or with a whitespace-only slug — the same LLM/caller bugs as the proxied path: model tool call with a blank action name.

Common situations: Agent harness forwarding model output unvalidated into the direct path; slug assembled from parts where one was empty; direct-mode scripts looping over a list containing blank entries.

Related errors


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