tinyhumansai/openhuman · error · anyhow::Error

composio.authorize: {AUTHORIZE_OAUTH_SCOPES_FIELD} entries m

Error message

composio.authorize: {AUTHORIZE_OAUTH_SCOPES_FIELD} entries must be strings

What it means

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.

Source

Thrown at src/openhuman/integrations/composio/client.rs:628

        }
    }
    Ok(())
}

fn append_missing_oauth_scopes(value: &mut Value, required: &[&str]) -> anyhow::Result<()> {
    let mut scopes = match value {
        Value::Null => Vec::new(),
        Value::String(raw) => raw
            .split(|ch: char| ch == ',' || ch.is_whitespace())
            .map(str::trim)
            .filter(|s| !s.is_empty())
            .map(ToString::to_string)
            .collect(),
        Value::Array(items) => {
            let mut out = Vec::with_capacity(items.len() + required.len());
            for item in items {
                let Some(scope) = item.as_str() else {
                    anyhow::bail!(
                        "composio.authorize: {AUTHORIZE_OAUTH_SCOPES_FIELD} entries must be strings"
                    );
                };
                let scope = scope.trim();
                if !scope.is_empty() {
                    out.push(scope.to_string());
                }
            }
            out
        }
        _ => {
            anyhow::bail!(
                "composio.authorize: {AUTHORIZE_OAUTH_SCOPES_FIELD} must be a string or array"
            );
        }
    };

    for scope in required {

View on GitHub (pinned to 7491200858)

Solutions

  1. Coerce every scope element to a string (and drop empties) before calling authorize
  2. Prefer passing oauth_scopes as a single comma- or space-separated string — the client splits and trims it itself
  3. Validate the scopes shape where the config is loaded, so bad data fails with file/field context

Example fix

// before
let extra = json!({ "oauth_scopes": ["repo", 42] });
client.authorize("github", Some(extra)).await?;

// after — sanitize to strings before the call
let scopes: Vec<String> = raw_scopes
    .iter()
    .filter_map(|v| v.as_str().map(str::trim))
    .filter(|s| !s.is_empty())
    .map(ToString::to_string)
    .collect();
let extra = json!({ "oauth_scopes": scopes });
client.authorize("github", Some(extra)).await?;
Defensive patterns

Strategy: type-guard

Validate before calling

fn sanitize_scopes(value: &serde_json::Value) -> Vec<String> {
    value
        .as_array()
        .map(|items| {
            items
                .iter()
                .filter_map(|v| v.as_str().map(str::trim))
                .filter(|s| !s.is_empty())
                .map(ToString::to_string)
                .collect()
        })
        .unwrap_or_default()
}

Type guard

fn scopes_entries_are_strings(value: &serde_json::Value) -> bool {
    value.as_array().is_some_and(|items| {
        items.iter().all(|v| v.is_string() || v.is_null())
    })
}

Prevention

When it happens

Trigger: 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"}].

Common situations: 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.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17). Data as JSON: /api/errors/2b441cbcc318d4c4. Report an issue: GitHub.