tinyhumansai/openhuman · error · anyhow::Error

toolkit `{toolkit}` is not connected. Connected toolkits: [{

Error message

toolkit `{toolkit}` is not connected. Connected toolkits: [{}]

What it means

render_integrations_agent (src/openhuman/agent/debug/mod.rs:272) looks up the requested toolkit in the agent's connected_integrations with a case-insensitive match AND `ci.connected == true`. A miss builds the connected-toolkit list (connected entries only) and errors with it, so the message doubles as a discovery aid.

Source

Thrown at src/openhuman/agent/debug/mod.rs:272

/// dynamic prompt builder.
async fn render_integrations_agent(config: &Config, toolkit: &str) -> Result<DumpedPrompt> {
    let mut agent = Agent::from_config_for_agent(config, INTEGRATIONS_AGENT_ID)
        .with_context(|| format!("building integrations_agent session for `{toolkit}`"))?;
    agent.fetch_connected_integrations().await;

    let mut integration = agent
        .connected_integrations()
        .iter()
        .find(|ci| ci.connected && ci.toolkit.eq_ignore_ascii_case(toolkit))
        .cloned()
        .ok_or_else(|| {
            let connected: Vec<String> = agent
                .connected_integrations()
                .iter()
                .filter(|ci| ci.connected)
                .map(|ci| ci.toolkit.clone())
                .collect();
            anyhow!(
                "toolkit `{toolkit}` is not connected. Connected toolkits: [{}]",
                connected.join(", ")
            )
        })?;

    // Resolve the live client kind via the mode-aware factory so a
    // direct-mode user can still render the prompt even without a
    // backend session (#1710 Wave 2). Backend mode keeps the existing
    // `fetch_toolkit_actions` round-trip; direct mode skips the
    // refresh (no backend allowlist to consult) and keeps the cached
    // catalogue, mirroring `ComposioListToolsTool`'s short-circuit.
    use crate::openhuman::integrations::composio::client::{
        create_composio_client, ComposioClientKind,
    };
    let client_kind = create_composio_client(config)
        .map_err(|e| anyhow!("composio client unavailable — is the user signed in? ({e})"))?;

    // Refresh the action catalogue for this toolkit at prompt-generation

View on GitHub (pinned to a221052e0d)

Solutions

  1. Pick a toolkit from the error's Connected toolkits list.
  2. Run `composio list_connection` to see live connection state and connect the toolkit you want.
  3. Reconnect a toolkit showing as known-but-disconnected, then retry the dump.
  4. If the list is empty but you expect connections, sign in / refresh so the connected_integrations snapshot repopulates.

Example fix

# before
... --agent integrations_agent --toolkit google   # error lists: [gmail, notion]
# after
... --agent integrations_agent --toolkit gmail
Defensive patterns

Strategy: validation

Validate before calling

let connected: Vec<String> = agent
    .connected_integrations()
    .iter()
    .filter(|ci| ci.connected)
    .map(|ci| ci.toolkit.clone())
    .collect();
if !connected.iter().any(|t| t.eq_ignore_ascii_case(&toolkit)) {
    anyhow::bail!("toolkit `{toolkit}` not connected; connected: {connected:?}");
}

Type guard

fn is_connected_toolkit(agent: &AgentHandle, toolkit: &str) -> bool {
    agent
        .connected_integrations()
        .iter()
        .any(|ci| ci.connected && ci.toolkit.eq_ignore_ascii_case(toolkit))
}

Try / catch

match render_integrations_agent(&config, toolkit).await {
    Err(e) if e.to_string().contains("is not connected") => {
        // Parse the Connected toolkits list from the message, prompt the user
        // to pick one or connect it, then retry with a valid toolkit.
    }
    other => other,
}

Prevention

When it happens

Trigger: Prompt-dumping `integrations_agent --toolkit X` where X is not connected at all, is known but whose connection is currently disabled/disconnected (ci.connected false), or where the backend session's connected_integrations snapshot is stale.

Common situations: Toolkit was revoked/disconnected after the app started; user guesses a toolkit name ("google" vs "gmail"); case variants are fine but prefixes/aliases are not; backend returned an empty snapshot so everything appears disconnected.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16). Data as JSON: /api/errors/62366a8b688c2f37. Report an issue: GitHub.