{"record":{"id":"5380d1470e996e27","repo":"tinyhumansai/openhuman","slug":"composio-authorize-toolkit-must-not-be-empty","errorCode":null,"errorMessage":"composio.authorize: toolkit must not be empty","messagePattern":"composio\\.authorize: toolkit must not be empty","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/openhuman/integrations/composio/client.rs","lineNumber":90,"sourceCode":"            .await\n    }\n\n    /// `POST /agent-integrations/composio/authorize` — begin an OAuth\n    /// handoff for `toolkit` and return the hosted `connectUrl` the user\n    /// must open in a browser.\n    ///\n    /// `extra_params` is an optional JSON object whose key/value pairs are\n    /// merged into the request body. Some toolkits (e.g. `whatsapp`) require\n    /// additional fields (e.g. `waba_id`) that Composio will reject the\n    /// authorization without.\n    pub async fn authorize(\n        &self,\n        toolkit: &str,\n        extra_params: Option<serde_json::Value>,\n    ) -> Result<ComposioAuthorizeResponse> {\n        let toolkit = toolkit.trim();\n        if toolkit.is_empty() {\n            anyhow::bail!(\"composio.authorize: toolkit must not be empty\");\n        }\n        tracing::debug!(toolkit = %toolkit, has_extra_params = extra_params.is_some(), \"[composio] authorize\");\n        let mut body = serde_json::json!({ \"toolkit\": toolkit });\n        if let Some(extra) = extra_params {\n            const RESERVED: &[&str] = &[\"toolkit\", \"toolkit_version\", \"auth\", \"client_id\"];\n            let extra_obj = extra.as_object().ok_or_else(|| {\n                anyhow::anyhow!(\"composio.authorize: extra_params must be a JSON object\")\n            })?;\n            let obj = body.as_object_mut().ok_or_else(|| {\n                anyhow::anyhow!(\"composio.authorize: internal payload must be an object\")\n            })?;\n            for (k, v) in extra_obj {\n                if RESERVED.contains(&k.as_str()) {\n                    anyhow::bail!(\n                        \"composio.authorize: extra_params cannot override reserved key '{k}'\"\n                    );\n                }\n                obj.insert(k.clone(), v.clone());","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/src/openhuman/integrations/composio/client.rs#L72-L108","documentation":"ComposioClient::authorize rejects a toolkit name that is empty after trimming, before the POST /agent-integrations/composio/authorize request is built. It is a precondition guard: the backend would reject the call anyway, so the client fails fast with a precise message.","triggerScenarios":"Calling client.authorize(\"\", extra_params) or with a toolkit of only whitespace — usually an unselected value from a UI dropdown or an unpopulated config field.","commonSituations":"Frontend submits the connection form before a toolkit was chosen; toolkit id read from a stale/renamed config key that no longer exists; data-entry path that trims user input to nothing.","solutions":["Make the toolkit selection mandatory in the calling UI/flow before authorize is reachable","Trim and validate the toolkit id at the boundary (request parsing / RPC handler) with a descriptive error","If toolkit ids come from a catalog list, validate against the listed ids to also catch typos"],"exampleFix":"// before\nlet resp = client.authorize(toolkit_from_form.as_str(), None).await?;\n\n// after\nlet toolkit = toolkit_from_form.trim();\nif toolkit.is_empty() {\n    return Err(anyhow::anyhow!(\"select a toolkit before authorizing\"));\n}\nlet resp = client.authorize(toolkit, None).await?;","handlingStrategy":"validation","validationCode":"let toolkit = toolkit.trim();\nif toolkit.is_empty() {\n    return Err(anyhow::anyhow!(\"select a toolkit before authorizing\"));\n}\nlet resp = client.authorize(toolkit, extra_params).await?;","typeGuard":"fn is_non_empty_toolkit(s: &str) -> bool {\n    !s.trim().is_empty()\n}","tryCatchPattern":null,"preventionTips":["Make toolkit a required field in the connection-request schema with a descriptive error","Populate toolkit pickers from the toolkits catalog so only valid ids can be submitted","Trim user input at the RPC boundary so whitespace-only cannot reach authorize"],"tags":["composio","authorize","validation","empty-string","argument-validation"],"backgroundTag":"empty-string-argument","analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","schemaVersion":2},"datasetVersion":"2026-08-23T13:39:53.451Z"}