{"record":{"id":"2b441cbcc318d4c4","repo":"tinyhumansai/openhuman","slug":"composio-authorize-authorize-oauth-scopes-field","errorCode":null,"errorMessage":"composio.authorize: {AUTHORIZE_OAUTH_SCOPES_FIELD} entries must be strings","messagePattern":"composio\\.authorize: (.+?) entries must be strings","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/openhuman/integrations/composio/client.rs","lineNumber":628,"sourceCode":"        }\n    }\n    Ok(())\n}\n\nfn append_missing_oauth_scopes(value: &mut Value, required: &[&str]) -> anyhow::Result<()> {\n    let mut scopes = match value {\n        Value::Null => Vec::new(),\n        Value::String(raw) => raw\n            .split(|ch: char| ch == ',' || ch.is_whitespace())\n            .map(str::trim)\n            .filter(|s| !s.is_empty())\n            .map(ToString::to_string)\n            .collect(),\n        Value::Array(items) => {\n            let mut out = Vec::with_capacity(items.len() + required.len());\n            for item in items {\n                let Some(scope) = item.as_str() else {\n                    anyhow::bail!(\n                        \"composio.authorize: {AUTHORIZE_OAUTH_SCOPES_FIELD} entries must be strings\"\n                    );\n                };\n                let scope = scope.trim();\n                if !scope.is_empty() {\n                    out.push(scope.to_string());\n                }\n            }\n            out\n        }\n        _ => {\n            anyhow::bail!(\n                \"composio.authorize: {AUTHORIZE_OAUTH_SCOPES_FIELD} must be a string or array\"\n            );\n        }\n    };\n\n    for scope in required {","sourceCodeStart":610,"sourceCodeEnd":646,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/src/openhuman/integrations/composio/client.rs#L610-L646","documentation":"merge_required_oauth_scopes normalizes the oauth_scopes field (AUTHORIZE_OAUTH_SCOPES_FIELD) of the authorize body before the request. When the field is an array but any element is not a JSON string (number, object, bool, null), it bails with this message client-side — the request never reaches Composio.","triggerScenarios":"Calling authorize with extra_params (or config data) where oauth_scopes is an array containing a non-string, e.g. [\"repo\", 42] or [\"repo\", {\"scope\":\"issues\"}].","commonSituations":"Scopes assembled by templating/serialization that produced numbers or nested objects; provider config copied from YAML where a scope entry parsed as a non-string; LLM-generated scope arrays with mixed types.","solutions":["Coerce every scope element to a string (and drop empties) before calling authorize","Prefer passing oauth_scopes as a single comma- or space-separated string — the client splits and trims it itself","Validate the scopes shape where the config is loaded, so bad data fails with file/field context"],"exampleFix":"// before\nlet extra = json!({ \"oauth_scopes\": [\"repo\", 42] });\nclient.authorize(\"github\", Some(extra)).await?;\n\n// after — sanitize to strings before the call\nlet scopes: Vec<String> = raw_scopes\n    .iter()\n    .filter_map(|v| v.as_str().map(str::trim))\n    .filter(|s| !s.is_empty())\n    .map(ToString::to_string)\n    .collect();\nlet extra = json!({ \"oauth_scopes\": scopes });\nclient.authorize(\"github\", Some(extra)).await?;","handlingStrategy":"type-guard","validationCode":"fn sanitize_scopes(value: &serde_json::Value) -> Vec<String> {\n    value\n        .as_array()\n        .map(|items| {\n            items\n                .iter()\n                .filter_map(|v| v.as_str().map(str::trim))\n                .filter(|s| !s.is_empty())\n                .map(ToString::to_string)\n                .collect()\n        })\n        .unwrap_or_default()\n}","typeGuard":"fn scopes_entries_are_strings(value: &serde_json::Value) -> bool {\n    value.as_array().is_some_and(|items| {\n        items.iter().all(|v| v.is_string() || v.is_null())\n    })\n}","tryCatchPattern":null,"preventionTips":["Pass oauth_scopes as one comma/space-separated string — the client splits it and this whole class vanishes","Validate scope arrays where config is deserialized, failing with the offending index","Never build scope arrays from untyped template output without a stringify pass"],"tags":["composio","authorize","oauth","scopes","type-validation"],"backgroundTag":"json-type-mismatch","analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}