tinyhumansai/openhuman · error · anyhow::Error
composio.execute_tool_once: tool slug must not be empty
Error message
composio.execute_tool_once: tool slug must not be empty
What it means
execute_tool_once is the caller-owns-retry entry point (used by auth_retry so a logical tool call retries exactly once); like execute_tool it rejects a tool slug that is empty after trimming, before egress disclosure and argument preparation. The guard keeps the disjoint entry points consistent: no path can send a blank slug.
Source
Thrown at src/openhuman/integrations/composio/client.rs:237
}
}
Ok(resp)
}
/// `POST /agent-integrations/composio/execute` — single, non-retrying
/// HTTP round-trip. Use this when the caller owns the retry loop
/// (e.g. `auth_retry`) to avoid double-retry. In particular,
/// [`super::auth_retry::execute_with_auth_retry`] uses this entry
/// point so its `must retry exactly once` contract still holds
/// after PR #1707 introduced the inner retry.
pub(crate) async fn execute_tool_once(
&self,
tool: &str,
arguments: Option<serde_json::Value>,
) -> Result<ComposioExecuteResponse> {
let tool = tool.trim();
if tool.is_empty() {
anyhow::bail!("composio.execute_tool_once: tool slug must not be empty");
}
// Egress spine (privacy epic S2, #4436): see `execute_tool`. This is the
// caller-owns-retry entry point (e.g. `auth_retry`), disjoint from
// `execute_tool`, so each logical tool call emits exactly once.
let egress = crate::openhuman::security::egress::EgressDescriptor::composio(tool);
// Local-only enforcement (privacy epic S7, #4441): same gate as
// `execute_tool` — this disjoint entry point must block too.
crate::openhuman::security::egress::enforce_egress(&egress)?;
crate::openhuman::security::egress::emit_external_transfer(egress);
let arguments = super::execute_prepare::prepare_execute_arguments(tool, arguments)
.map_err(anyhow::Error::msg)?;
tracing::debug!(tool = %tool, "[composio] execute_tool_once (no built-in retry)");
let body = json!({ "tool": tool, "arguments": arguments });
let result = self.post_execute_tool(&body).await;
match &result {
Ok(resp) => tracing::debug!(
tool = %tool,
successful = resp.successful,View on GitHub (pinned to 7491200858)
Solutions
- Validate the slug before entering your retry wrapper — this entry point exists for caller-owned control flow, so own the validation too
- Re-prompt or skip when the model produces a blank action name
- Assert non-empty where tool calls are deserialized from model output
Example fix
// before
let resp = client.execute_tool_once(slug, args).await?;
// after
let slug = slug.trim();
if slug.is_empty() {
return Err(anyhow::anyhow!("refusing to execute composio action with empty slug"));
}
let resp = client.execute_tool_once(slug, args).await?; Defensive patterns
Strategy: validation
Validate before calling
let tool = tool.trim();
if tool.is_empty() {
anyhow::bail!("cannot execute composio tool with an empty slug");
}
let resp = client.execute_tool_once(tool, arguments).await?; Type guard
fn is_non_empty_slug(s: &str) -> bool {
!s.trim().is_empty()
} Prevention
- This entry point exists for caller-owned retry loops — validate input before entering yours
- Share one slug validation helper across execute_tool and execute_tool_once call sites
- Assert non-empty in tests of your retry wrapper so regressions surface there first
When it happens
Trigger: Calling execute_tool_once("", arguments) directly, or auth_retry receiving a blank slug and forwarding it here. Typically the same caller bugs as execute_tool: LLM tool calls with missing action names.
Common situations: Custom retry wrappers that call execute_tool_once directly with unvalidated input; agent harnesses forwarding model output verbatim; templated action names that rendered empty.
Related errors
- composio.execute_tool: tool slug must not be empty
- composio.execute_tool: tool slug must not be empty
- composio.authorize: toolkit must not be empty
- composio.delete_connection: connectionId must not be empty
- composio.create_trigger: slug must not be empty
AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17).
Data as JSON: /api/errors/a6604d558be324aa.
Report an issue: GitHub.