tinyhumansai/openhuman · error

provider is required

Error message

provider is required

What it means

Thrown by BackendOAuthClient::login_url when the provider argument is empty after trimming whitespace and stripping leading/trailing slashes. The provider string becomes a path segment ("auth/{provider}/login"), so an empty value would build a malformed login URL; the guard fails fast instead. It is a pure caller-input check — no network is touched.

Source

Thrown at src/api/rest.rs:452

    /// drive a non-JSON request shape (e.g. `multipart/form-data` uploads
    /// for cloud STT) without re-implementing TLS/proxy plumbing.
    pub fn raw_client(&self) -> &Client {
        &self.client
    }

    /// Resolve a backend-relative path against the configured base URL.
    /// Mirrors what `authed_json` does internally so callers using
    /// `raw_client()` don't have to assemble URLs by hand.
    pub fn url_for(&self, path: &str) -> Result<Url> {
        self.base
            .join(path.trim_start_matches('/'))
            .with_context(|| format!("build URL for {path}"))
    }

    /// Returns the URL for initiating a login flow for a specific provider.
    pub fn login_url(&self, provider: &str) -> Result<Url> {
        let p = provider.trim().trim_matches('/');
        anyhow::ensure!(!p.is_empty(), "provider is required");
        self.base
            .join(&format!("auth/{p}/login"))
            .context("build login URL")
    }

    /// Initiates an OAuth connection flow for the current user and a specific provider.
    pub async fn connect(
        &self,
        provider: &str,
        bearer_jwt: &str,
        skill_id: Option<&str>,
        response_type: Option<&str>,
        encryption_mode: Option<&str>,
    ) -> Result<ConnectResponse> {
        let p = provider.trim().trim_matches('/');
        anyhow::ensure!(!p.is_empty(), "provider is required");
        let query = {
            let mut serializer = url::form_urlencoded::Serializer::new(String::new());

View on GitHub (pinned to a221052e0d)

Solutions

  1. Pass a concrete provider id such as "telegram", "google", "discord"
  2. Validate the provider selection at the UI/CLI boundary before invoking login_url
  3. Default to a known provider when the field is optional in your flow

Example fix

// before
let url = client.login_url(&selected_provider)?; // selected_provider = ""

// after
let provider = selected_provider.trim().trim_matches('/');
anyhow::ensure!(!provider.is_empty(), "select a provider first");
let url = client.login_url(provider)?;
Defensive patterns

Strategy: validation

Validate before calling

let provider = provider.trim().trim_matches('/');
anyhow::ensure!(!provider.is_empty(), "provider must be selected before building login URL");
let url = client.login_url(provider)?;

Type guard

fn is_provider_slug(s: &str) -> bool {
    let t = s.trim().trim_matches('/');
    !t.is_empty() && t.chars().all(|c| c.is_ascii_alphanumeric() || c == '-')
}

Prevention

When it happens

Trigger: Calling login_url(""), login_url(" "), or login_url("/") — e.g. the provider slug came from a config field, CLI flag, or UI selection that was never filled in.

Common situations: A provider dropdown in the UI submitted with no selection, a config template shipping an empty [[provider]] entry, or a default provider constant that was renamed to an empty string.

Related errors


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