{"record":{"id":"8950f49e024f331c","repo":"windmill-labs/windmill","slug":"invalid-token-endpoint-url-e","errorCode":null,"errorMessage":"Invalid token endpoint URL: {e}","messagePattern":"Invalid token endpoint URL: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/windmill-oauth/src/lib.rs","lineNumber":336,"sourceCode":"#[derive(Deserialize)]\npub struct OAuthCallback {\n    pub code: String,\n    pub state: String,\n}\n\n/// Build a basic OAuth client from configuration\npub fn build_basic_client(\n    name: String,\n    config: OAuthConfig,\n    client_params: OAuthClient,\n    login: bool,\n    base_url: &str,\n    override_callback: Option<String>,\n) -> error::Result<(String, OClient)> {\n    let auth_url = Url::parse(&config.auth_url)\n        .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","sourceCodeStart":318,"sourceCodeEnd":354,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/backend/windmill-oauth/src/lib.rs#L318-L354","documentation":"Same as the authorization URL check but for the OAuth token endpoint (config.token_url). build_basic_client parses it before constructing the oauth2 OClient; an unparseable value aborts client creation.","triggerScenarios":"build_basic_client / build_client_credentials_oauth_client invoked with a config whose token_url is empty, scheme-less, or otherwise invalid per Url::parse.","commonSituations":"Token endpoint left blank in provider settings while auth URL was filled in; copying only the path (/login/oauth/access_token); self-hosted provider (Keycloak, Auth0) token URL typo'd or pointing at a bare host.","solutions":["Correct the token_url in the OAuth config to a full absolute URL, e.g. https://github.com/login/oauth/access_token","Confirm the token endpoint against the provider's OAuth documentation (some providers use /oauth/token, others /api/oauth.v2.access)","Check that the value is not empty — empty strings fail Url::parse with RelativeUrlWithoutBase","Restart/re-save the provider config after fixing so the client is rebuilt"],"exampleFix":"// before\ntoken_url = \"https://github.com\"  // missing path\n// after\ntoken_url = \"https://github.com/login/oauth/access_token\"","handlingStrategy":"validation","validationCode":"fn valid_token_url(s: &str) -> bool { Url::parse(s).map(|u| u.scheme().starts_with(\"http\")).unwrap_or(false) }","typeGuard":"fn is_absolute_http_url(s: &str) -> bool {\n    Url::parse(s).map(|u| matches!(u.scheme(), \"http\" | \"https\")).unwrap_or(false)\n}","tryCatchPattern":"match build_basic_client(&config, login, base_url, None) {\n    Ok(v) => v,\n    Err(e) if e.to_string().contains(\"Invalid token endpoint URL\") => {\n        // inspect config.token_url, fix and retry\n        return Err(e);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Validate both auth_url and token_url together when saving a provider config","Copy token endpoints directly from provider docs to avoid path typos","Never leave token_url empty — some admin UIs allow partial configs"],"tags":["oauth","url-parsing","configuration"],"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-08T15:18:49.778Z"}