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
- Fix the instance BASE_URL to a full absolute URL like https://app.example.com and restart Windmill
- If using override_callback, provide an absolute URL including scheme
- 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
- Set BASE_URL with scheme (https://...) in every deployment environment
- Validate BASE_URL at backend startup and refuse to boot on an invalid value
- If using override_callback, always pass absolute URLs
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
- Invalid authorization endpoint URL: {e}
- Invalid token endpoint URL: {e}
- Invalid Slack authorization URL: {e}
- Invalid Slack token URL: {e}
- ${what} failed:\n${output}
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/488b1f2411059ef3.
Report an issue: GitHub.