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

  1. Unwrap e.getCause() to identify the failing step (null field vs timer validation vs JSON parse)
  2. Capture the actual token endpoint response with curl using the same client id/credential and compare against the expected access_token/expires_in shape
  3. If the IdP legitimately omits expires_in, switch to a provider implementation or endpoint variant that returns it
  4. 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

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


AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22). Data as JSON: /api/errors/6cc2d24f08547dd2. Report an issue: GitHub.