tinyhumansai/openhuman · error · anyhow::Error
composio direct api key must not be empty
Error message
composio direct api key must not be empty
What it means
create_direct_composio_tool_for_api_key builds the direct-to-Composio (non-backend-proxied) tool from a user-supplied API key; a key that is empty after trimming is rejected before any client is constructed. The guard sits at factory time so direct mode never starts with a credential that cannot work.
Source
Thrown at src/openhuman/integrations/composio/client.rs:730
/// HMAC-verified trigger fan-out, no `/agent-integrations/pricing`),
/// so most existing call-sites continue to use `Backend` for now.
/// Direct-mode integration of the full surface (especially trigger
/// webhooks) is a follow-up.
pub enum ComposioClientKind {
Backend(ComposioClient),
/// Held inside an `Arc` so the variant stays cheap to clone — this
/// matches the rest of the tool registry which juggles
/// `Arc<dyn Tool>` for the same direct-mode tool elsewhere.
Direct(Arc<crate::openhuman::tools::ComposioTool>),
}
pub(crate) fn create_direct_composio_tool_for_api_key(
config: &crate::openhuman::config::Config,
api_key: &str,
) -> anyhow::Result<Arc<crate::openhuman::tools::ComposioTool>> {
let api_key = api_key.trim();
if api_key.is_empty() {
anyhow::bail!("composio direct api key must not be empty");
}
// The direct client takes a `SecurityPolicy` for `Tool::execute`
// gating, but the factory's job is only to materialize a *client*
// — it does not actually invoke `execute()` itself, so the
// default policy is sufficient here. Callers that go through
// the `Tool` surface re-acquire the live policy from their own
// context.
let security = Arc::new(crate::openhuman::security::SecurityPolicy::default());
#[cfg(debug_assertions)]
let tool = match (
std::env::var("OPENHUMAN_COMPOSIO_DIRECT_BASE_V2").ok(),
std::env::var("OPENHUMAN_COMPOSIO_DIRECT_BASE_V3").ok(),
) {
(Some(base_v2), Some(base_v3)) => {
crate::openhuman::tools::ComposioTool::new_with_base_urls_for_loopback(
api_key,
Some(config.composio.entity_id.as_str()),View on GitHub (pinned to 7491200858)
Solutions
- Set the real Composio API key in the config/environment before enabling direct mode
- Validate the key non-empty at config-load time with a message naming the exact setting, instead of failing later at tool creation
- If the key is genuinely absent, stay on the backend-proxied path rather than selecting direct mode
Example fix
// before
let tool = create_direct_composio_tool_for_api_key(&config, cfg.composio_api_key.as_str()).await?;
// after — validate at config load with a named setting
let key = cfg.composio_api_key.trim();
if key.is_empty() {
anyhow::bail!("composio direct mode requires composio_api_key to be set");
}
let tool = create_direct_composio_tool_for_api_key(&config, key)?; Defensive patterns
Strategy: validation
Validate before calling
let api_key = cfg.composio_api_key.trim();
if api_key.is_empty() {
anyhow::bail!(
"composio direct mode requires a non-empty API key (setting: composio_api_key / env var)"
);
}
let tool = create_direct_composio_tool_for_api_key(&config, api_key)?; Type guard
fn api_key_present(cfg: &Config) -> bool {
!cfg.composio_api_key.trim().is_empty()
} Prevention
- Validate the key at config load with a message naming the setting and env var
- Fail CI/startup checks when direct mode is selected without a key instead of deferring to first tool creation
- Trim pasted keys on save in the UI and reject whitespace-only input
When it happens
Trigger: Direct Composio mode enabled while the configured api_key is empty or whitespace — e.g. the key env var unset in CI but the config still selects direct mode, or a blank key saved from settings.
Common situations: Environment variable for the key missing in CI/containers; UI allowed saving an empty key; key field contains only whitespace from a bad copy/paste; config default of "" left in place.
Understand the failure class
Background: "API key is required" / "API key not found" / "No API key was set": the missing-api-key error family across 16 libraries — this error's family across 16 libraries.
Related errors
- No usable credentials for '{slug}', which OpenHuman selected
- No usable credentials for '{slug}', which OpenHuman selected
- composio.execute_tool: tool slug must not be empty
- composio.authorize: toolkit must not be empty
- composio.delete_connection: connectionId must not be empty
AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17).
Data as JSON: /api/errors/8fb409c06c54a329.
Report an issue: GitHub.