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 = rawView on GitHub (pinned to 7491200858)
Solutions
- Validate and trim the slug in the caller before invoking direct mode, and re-prompt or skip when blank
- Filter blank entries when building tool lists for batch execution
- 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
- Validate slugs once in the agent harness so both proxied and direct paths receive clean input
- Filter blank slugs when iterating tool lists for batch execution
- Treat an empty action name from a model as a re-prompt condition, not an error to surface to users
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
- composio.execute_tool: tool slug must not be empty
- composio.execute_tool_once: tool slug must not be empty
- composio direct authorize: toolkit must not be empty
- composio.execute_tool: tool slug must not be empty
- composio.authorize: toolkit must not be empty
AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17).
Data as JSON: /api/errors/5462ba4b9f61f020.
Report an issue: GitHub.