windmill-labs/windmill · error

Invalid redirect URL: {e}

Error message

Invalid redirect URL: {e}

What it means

After building the OAuth client, build_basic_client parses the computed redirect URL (base_url + /user/login_callback/{name}, or an override_callback) and fails if it is not a valid absolute URL. This usually means the instance BASE_URL itself is malformed.

Source

Thrown at backend/windmill-oauth/src/lib.rs:352

        .map_err(|e| anyhow!("Invalid authorization endpoint URL: {e}"))?;
    let token_url =
        Url::parse(&config.token_url).map_err(|e| anyhow!("Invalid token endpoint URL: {e}"))?;

    let redirect_url = if login {
        format!("{base_url}/user/login_callback/{name}")
    } else if let Some(callback) = override_callback {
        callback
    } else {
        format!("{base_url}/oauth/callback/{name}")
    };

    let mut client = OClient::new(client_params.id, auth_url, token_url);
    if config.req_body_auth.unwrap_or(false) {
        client.set_auth_type(AuthType::RequestBody);
    }
    client.set_client_secret(client_params.secret.clone());
    client.set_redirect_url(
        Url::parse(&redirect_url).map_err(|e| anyhow!("Invalid redirect URL: {e}"))?,
    );

    Ok((name.to_string(), client))
}

/// Build a Slack OAuth client with custom credentials
pub async fn build_slack_client(
    client_id: &str,
    client_secret: &str,
    _workspace_id: &str,
) -> error::Result<OClient> {
    let auth_url = Url::parse("https://slack.com/oauth/v2/authorize")
        .map_err(|e| anyhow!("Invalid Slack authorization URL: {e}"))?;
    let token_url = Url::parse("https://slack.com/api/oauth.v2.access")
        .map_err(|e| anyhow!("Invalid Slack token URL: {e}"))?;

    let base_url = (**BASE_URL.load()).clone();
    let redirect_url = format!("{}/oauth/callback_slack", base_url);

View on GitHub (pinned to e474e8803c)

Solutions

  1. Fix the instance BASE_URL to a full absolute URL like https://app.example.com and restart Windmill
  2. If using override_callback, provide an absolute URL including scheme
  3. Check the BASE_URL env var is not empty or containing stray quotes/whitespace

Example fix

// before (docker env)
BASE_URL=app.example.com
// after
BASE_URL=https://app.example.com
Defensive patterns

Strategy: validation

Validate before calling

fn valid_base() -> bool { Url::parse(&base_url).map(|u| u.scheme().starts_with("http")).unwrap_or(false) }
if !valid_base() { panic!("BASE_URL must be an absolute URL, got: {base_url}"); }

Type guard

fn is_valid_redirect(u: &str) -> bool {
    Url::parse(u).map(|r| matches!(r.scheme(), "http" | "https")).unwrap_or(false)
}

Try / catch

match build_basic_client(&config, login, base_url, override_callback) {
    Ok(v) => v,
    Err(e) if e.to_string().contains("Invalid redirect URL") => {
        // base_url or override_callback is malformed; fail fast with a clear config error
        anyhow::bail!("Set BASE_URL to an absolute https URL; current: {base_url}");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: build_basic_client called with a base_url lacking a scheme (e.g. 'app.example.com' or empty), or an override_callback that is not an absolute URL.

Common situations: BASE_URL environment variable set without https:// in a self-hosted deployment; override callback configured as a relative path; BASE_URL empty during early startup or misconfigured Docker env.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/488b1f2411059ef3. Report an issue: GitHub.