tinyhumansai/openhuman · error · anyhow::Error
composio.delete_connection: connectionId must not be empty
Error message
composio.delete_connection: connectionId must not be empty
What it means
ComposioClient::delete_connection rejects a connectionId that is empty after trimming, before issuing DELETE /agent-integrations/composio/connections/{id}. The id must come from a previous authorize or list_connections result; the guard stops a meaningless DELETE against the bare /connections/ URL.
Source
Thrown at src/openhuman/integrations/composio/client.rs:126
obj.insert(k.clone(), v.clone());
}
}
merge_required_oauth_scopes(&mut body, toolkit)?;
self.inner
.post::<ComposioAuthorizeResponse>("/agent-integrations/composio/authorize", &body)
.await
}
/// `DELETE /agent-integrations/composio/connections/{id}`.
///
/// The backend verifies that the caller owns the connection before
/// deleting it. We call this via `POST` with a synthetic `_method`
/// body because [`IntegrationClient`] does not currently expose a
/// generic `delete()` — the backend accepts the method override.
pub async fn delete_connection(&self, connection_id: &str) -> Result<ComposioDeleteResponse> {
let connection_id = connection_id.trim();
if connection_id.is_empty() {
anyhow::bail!("composio.delete_connection: connectionId must not be empty");
}
tracing::debug!(connection_id = %connection_id, "[composio] delete_connection");
// Fall through to the reusable raw HTTP delete helper below.
self.raw_delete::<ComposioDeleteResponse>(&format!(
"/agent-integrations/composio/connections/{connection_id}"
))
.await
}
// ── Tools ───────────────────────────────────────────────────────
/// `GET /agent-integrations/composio/tools?toolkits=<csv>&tags=<csv>` — fetch
/// OpenAI function-calling schemas. Omit `toolkits` to get every enabled
/// toolkit's tools. `tags` narrows by Composio action tag (OR semantics —
/// multiple tags broaden the result).
pub async fn list_tools(
&self,
toolkits: Option<&[String]>,View on GitHub (pinned to 7491200858)
Solutions
- Fetch the connection list first and delete by an id taken from it
- Make the id a required parameter in the calling RPC/UI path so an empty value is caught with a better message
- Disable the delete action in the UI when the row has no id
Example fix
// before
client.delete_connection(conn.id.as_deref().unwrap_or("")).await?;
// after
let Some(id) = conn.id.as_deref().map(str::trim).filter(|s| !s.is_empty()) else {
anyhow::bail!("connection has no id; refresh the connection list");
};
client.delete_connection(id).await?; Defensive patterns
Strategy: validation
Validate before calling
let Some(id) = connection_id.as_deref().map(str::trim).filter(|s| !s.is_empty()) else {
anyhow::bail!("connection id is required to delete a connection");
};
client.delete_connection(id).await?; Type guard
fn is_non_empty_id(s: &str) -> bool {
!s.trim().is_empty()
} Prevention
- Source connection ids only from list_connections/authorize responses
- Disable delete actions in the UI until the row has a populated id
- Type connection ids as NonEmptyString-style wrappers at the boundary if your codebase supports it
When it happens
Trigger: Calling delete_connection("") or with a whitespace-only id — usually a delete fired from a UI row whose connection id field was never populated, or a flow that deletes before fetching connections.
Common situations: Frontend row model missing the id mapping; deleting from a stale/empty connections list; race where the row was cleared before the delete handler ran.
Related errors
- composio.execute_tool: tool slug must not be empty
- composio.authorize: toolkit must not be empty
- composio.execute_tool: tool slug must not be empty
- composio.execute_tool_once: tool slug 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/0f4f144323b5450c.
Report an issue: GitHub.