{"record":{"id":"488b1f2411059ef3","repo":"windmill-labs/windmill","slug":"invalid-redirect-url-e","errorCode":null,"errorMessage":"Invalid redirect URL: {e}","messagePattern":"Invalid redirect URL: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/windmill-oauth/src/lib.rs","lineNumber":352,"sourceCode":"        .map_err(|e| anyhow!(\"Invalid authorization endpoint URL: {e}\"))?;\n    let token_url =\n        Url::parse(&config.token_url).map_err(|e| anyhow!(\"Invalid token endpoint URL: {e}\"))?;\n\n    let redirect_url = if login {\n        format!(\"{base_url}/user/login_callback/{name}\")\n    } else if let Some(callback) = override_callback {\n        callback\n    } else {\n        format!(\"{base_url}/oauth/callback/{name}\")\n    };\n\n    let mut client = OClient::new(client_params.id, auth_url, token_url);\n    if config.req_body_auth.unwrap_or(false) {\n        client.set_auth_type(AuthType::RequestBody);\n    }\n    client.set_client_secret(client_params.secret.clone());\n    client.set_redirect_url(\n        Url::parse(&redirect_url).map_err(|e| anyhow!(\"Invalid redirect URL: {e}\"))?,\n    );\n\n    Ok((name.to_string(), client))\n}\n\n/// Build a Slack OAuth client with custom credentials\npub async fn build_slack_client(\n    client_id: &str,\n    client_secret: &str,\n    _workspace_id: &str,\n) -> error::Result<OClient> {\n    let auth_url = Url::parse(\"https://slack.com/oauth/v2/authorize\")\n        .map_err(|e| anyhow!(\"Invalid Slack authorization URL: {e}\"))?;\n    let token_url = Url::parse(\"https://slack.com/api/oauth.v2.access\")\n        .map_err(|e| anyhow!(\"Invalid Slack token URL: {e}\"))?;\n\n    let base_url = (**BASE_URL.load()).clone();\n    let redirect_url = format!(\"{}/oauth/callback_slack\", base_url);","sourceCodeStart":334,"sourceCodeEnd":370,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/backend/windmill-oauth/src/lib.rs#L334-L370","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before (docker env)\nBASE_URL=app.example.com\n// after\nBASE_URL=https://app.example.com","handlingStrategy":"validation","validationCode":"fn valid_base() -> bool { Url::parse(&base_url).map(|u| u.scheme().starts_with(\"http\")).unwrap_or(false) }\nif !valid_base() { panic!(\"BASE_URL must be an absolute URL, got: {base_url}\"); }","typeGuard":"fn is_valid_redirect(u: &str) -> bool {\n    Url::parse(u).map(|r| matches!(r.scheme(), \"http\" | \"https\")).unwrap_or(false)\n}","tryCatchPattern":"match build_basic_client(&config, login, base_url, override_callback) {\n    Ok(v) => v,\n    Err(e) if e.to_string().contains(\"Invalid redirect URL\") => {\n        // base_url or override_callback is malformed; fail fast with a clear config error\n        anyhow::bail!(\"Set BASE_URL to an absolute https URL; current: {base_url}\");\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["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"],"tags":["oauth","url-parsing","configuration","base-url"],"backgroundTag":"invalid-url-format","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}