karatelabs/karate · error · OAuth2Exception
Token refresh failed: expected JSON object but received
Error message
Token refresh failed: expected JSON object but received
What it means
After successfully parsing the refresh response, refreshToken() requires the JSON to be an object (a token payload with access_token etc.). If the body is a JSON array or primitive, it clears the cached token and throws OAuth2Exception identifying which non-object shape was received.
Solutions
- Confirm the URL is the token endpoint (RFC 6749) which returns a flat JSON object.
- Unwrap envelope responses before token management, or use a custom token response handler.
- Compare received shape (message says array vs primitive) against expected `{access_token, ...}`.
- Test the endpoint with curl to see the actual JSON body.
Example fix
// before
// tokenUrl points at /.well-known/openid-configuration returning object of URLs — wrong shape elsewhere
// after
// tokenUrl = "https://idp.example.com/oauth2/token" returning {"access_token":"...","token_type":"Bearer"} Defensive patterns
Strategy: try-catch
Validate before calling
// sanity-check endpoint shape once at startup
var body = JSON.parse(httpGet(tokenUrl).body); if (Array.isArray(body)) throw new Error('tokenUrl returns an array, not an object'); Try / catch
try { token = mgr.refresh(); } catch (OAuth2Exception e) { if (e.getMessage().contains('expected JSON object')) { log.error('Wrong endpoint or enveloped response: ' + e.getMessage()); fixEndpointOrUnwrap(); } else throw e; } Prevention
- Use the token endpoint, not discovery/JWKS/introspection URLs
- Ensure the response is a flat object {access_token, ...}
- Curl the endpoint to confirm the JSON shape before configuring
- Unwrap envelope-style responses at a lower layer if your IdP wraps payloads
When it happens
Trigger: Token endpoint responds with `[...]` or `"string"`/`123` — e.g. an introspection endpoint returning an array, or a misconfigured URL returning a JSON list of endpoints.
Common situations: Pointing the manager at an OIDC discovery document or JWKS (JSON object confusion aside) or other array-returning endpoint; custom idps returning wrapped payloads.
Related errors
- Token refresh failed: server returned invalid JSON response
- Token refresh failed:
- Authorization flow failed: " + e.getMessage()
- Missing 'authorizationUrl' in OAuth config
- Missing 'client_id' in OAuth config
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/bd4fa8747321aec1.
Report an issue: GitHub.
Appendix: source
Thrown at karate-core/src/main/java/io/karatelabs/http/OAuth2TokenManager.java:95
throw new OAuth2Exception(errorMessage);
}
Json json;
try {
json = Json.of(body);
} catch (Exception e) {
String errorMessage = "Token refresh failed: server returned invalid JSON response";
logger.error(errorMessage);
currentToken = null;
throw new OAuth2Exception(errorMessage);
}
if (!json.isObject()) {
String errorMessage = "Token refresh failed: expected JSON object but received " +
(json.isArray() ? "array" : "primitive value");
logger.error(errorMessage);
currentToken = null;
throw new OAuth2Exception(errorMessage);
}
Map<String, Object> data = json.asMap();
OAuth2Token newToken = OAuth2Token.fromMap(data);
storeToken(newToken);
logger.debug("Token refreshed successfully");
return newToken;
} catch (OAuth2Exception e) {
throw e;
} catch (Exception e) {
String errorMessage = "Token refresh failed: " + e.getMessage();
logger.error(errorMessage);
currentToken = null;
throw new OAuth2Exception(errorMessage, e);
}
}
/**View on GitHub (pinned to a22eb90246)