apache/hadoop · error · IOException
Unable to obtain access token from credential
Error message
Unable to obtain access token from credential
What it means
CredentialBasedAccessTokenProvider.refresh wraps any RuntimeException from the token exchange into IOException('Unable to obtain access token from credential') with the cause attached. It is the client-credentials counterpart of the refresh-token provider's wrapper: the HTTP call succeeded or failed, but a RuntimeException (NPE on a missing access_token/expires_in field, IllegalStateException from the timer on malformed expires_in, JSON mapping error) escaped before accessToken was set.
Source
Thrown at hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/web/oauth2/CredentialBasedAccessTokenProvider.java:137
httpPost.setEntity(new UrlEncodedFormEntity(pairs, StandardCharsets.UTF_8));
httpPost.setHeader(HttpHeaders.CONTENT_TYPE, URLENCODED);
try (CloseableHttpResponse response = client.execute(httpPost)) {
final int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
throw new IllegalArgumentException(
"Received invalid http response: " + statusCode + ", text = " +
EntityUtils.toString(response.getEntity()));
}
Map<?, ?> responseBody = JsonSerialization.mapReader().readValue(
EntityUtils.toString(response.getEntity()));
String newExpiresIn = responseBody.get(EXPIRES_IN).toString();
timer.setExpiresIn(newExpiresIn);
accessToken = responseBody.get(ACCESS_TOKEN).toString();
}
} catch (RuntimeException e) {
throw new IOException("Unable to obtain access token from credential", e);
} catch (Exception e) {
throw new IOException("Unable to obtain access token from credential", e);
}
}
}
View on GitHub (pinned to 2add963021)
Solutions
- Unwrap e.getCause() to identify the failing step (null field vs timer validation vs JSON parse)
- Capture the actual token endpoint response with curl using the same client id/credential and compare against the expected access_token/expires_in shape
- If the IdP legitimately omits expires_in, switch to a provider implementation or endpoint variant that returns it
- Add a one-time integration test that performs the exchange during deployment so schema drift is caught early
Defensive patterns
Strategy: retry
Try / catch
try {
return fs.open(p);
} catch (IOException e) {
if ("Unable to obtain access token from credential".equals(e.getMessage())
&& isTransient(e.getCause())) {
return retryWithBackoff(() -> fs.open(p));
}
throw e;
} Prevention
- Pin the expected token response schema in an integration test against the IdP
- Log e.getCause() fully — NPE on a missing field points at response-shape drift
- Prefer IdP endpoints that return both access_token and expires_in
When it happens
Trigger: refresh() where response handling blows up: 200 response whose JSON lacks access_token or expires_in (NPE at responseBody.get(...).toString()), malformed expires_in tripping timer.setExpiresIn, or unexpected response shape from the IdP.
Common situations: IdP changes its token response schema; endpoints returning error objects with HTTP 200; test stubs returning incomplete payloads; region/tenant URL changes altering response fields.
Related errors
- Exception while refreshing access token
- Received invalid http response: {statusCode}, text = {text}
- Unable to load OAuth2 connection factory.
- Credential has not been provided in configuration
- Received invalid http response: {statusCode}, text = {text}
AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22).
Data as JSON: /api/errors/6cc2d24f08547dd2.
Report an issue: GitHub.