karatelabs/karate · error · OAuth2Exception
Missing 'url' (token endpoint) in OAuth config
Error message
Missing 'url' (token endpoint) in OAuth config
What it means
exchangeCodeForToken() reads the token endpoint URL from the config key 'url'. Without it the authorization code cannot be exchanged for tokens, so an OAuth2Exception is thrown before any HTTP request is made. Note the key is the generic 'url', not something token-specific like 'tokenUrl'.
Solutions
- Add the provider's token endpoint under the exact key 'url' in the OAuth config map
- Check for near-miss keys like 'tokenUrl' or 'token_url' and rename to 'url'
- Validate the full required key set (authorizationUrl, client_id, url) before running the flow
Example fix
// before
config.put("tokenUrl", "https://idp/token");
// after
config.put("url", "https://idp/token"); Defensive patterns
Strategy: validation
Validate before calling
Object tokenUrl = config.get("url");
if (tokenUrl == null || tokenUrl.toString().isBlank()) {
throw new IllegalArgumentException("config.url (token endpoint) is required");
} Type guard
static String requireConfigKey(Map<String, Object> config, String key) {
Object v = config.get(key);
if (!(v instanceof String s) || s.isBlank()) {
throw new IllegalArgumentException("Missing '" + key + "' in OAuth config");
}
return s;
} Try / catch
try {
Token t = handler.token(code, verifier, redirectUri);
} catch (OAuth2Exception e) {
if (e.getMessage().contains("token endpoint")) {
throw new ConfigurationException("Add 'url' (token endpoint) to OAuth config", e);
}
throw e;
} Prevention
- Set the token endpoint under the exact key 'url' (not tokenUrl/token_url)
- Validate the full key set (authorizationUrl, client_id, url) before any flow step
- Reuse a shared config-builder that always emits all required keys
- Verify endpoint URLs against provider OIDC discovery metadata
When it happens
Trigger: token() -> exchangeCodeForToken() called with a config that has authorizationUrl and client_id but no 'url' key pointing at the token endpoint — common when the config was built for the authorize step only, or the key was named 'tokenUrl'.
Common situations: Splitting OAuth config across files where the token endpoint was dropped, renaming keys when migrating between auth handler implementations, or provider docs using 'token_endpoint' terminology.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- Missing 'authorizationUrl' in OAuth config
- Missing 'client_id' in OAuth config
- Invalid callbackPort: " + portValue
- Invalid callbackPort type: " + portValue.getClass()
- Invalid port in callbackPorts: " + parts[i]
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/cd356003e8af7776.
Report an issue: GitHub.
Appendix: source
Thrown at karate-core/src/main/java/io/karatelabs/http/AuthorizationCodeAuthHandler.java:174
// Add state for CSRF protection
String state = generateState();
url.append("&state=").append(urlEncode(state));
return url.toString();
}
/**
* Exchange authorization code for access token
*/
private OAuth2Token exchangeCodeForToken(
HttpRequestBuilder builder,
String code,
String codeVerifier,
String redirectUri
) {
String tokenUrl = (String) config.get("url");
if (tokenUrl == null) {
throw new OAuth2Exception("Missing 'url' (token endpoint) in OAuth config");
}
logger.debug("Exchanging authorization code for token...");
builder.url(tokenUrl);
builder.formField("grant_type", "authorization_code");
builder.formField("code", code);
builder.formField("redirect_uri", redirectUri);
builder.formField("client_id", config.get("client_id"));
builder.formField("code_verifier", codeVerifier);
// Optional client_secret (for confidential clients)
if (config.containsKey("client_secret")) {
builder.formField("client_secret", config.get("client_secret"));
}
builder.header("Accept", "application/json");
View on GitHub (pinned to a22eb90246)