apache/iceberg · error

Failed to refresh token

Error message

Failed to refresh token

What it means

OAuth2Util.refresh() retried refreshing the OAuth2 token via the token endpoint and all attempts failed, so the new token holder stayed null and a warning is logged. The client cannot obtain a valid access token, so subsequent authenticated REST catalog requests will fail with 401.

Solutions

  1. Re-obtain fresh credentials: fix client-credentials (client-id/client-secret) or refresh token in catalog properties and restart
  2. Verify network/DNS/proxy connectivity to the token endpoint and that the OAuth2 server URL is correct
  3. Check auth server logs/auditing for why the grant is rejected (invalid_grant, revoked token)
  4. Increase tokenRefreshNumRetries / retry window if the failure is transient
  5. Shorten session usage time so tokens are used before expiry; ensure a single token-refresh owner per process

Example fix

// before
.loadCatalog("rest", Map.of("uri", uri, "credential", "user:wrong-secret"))
// after
.loadCatalog("rest", Map.of("uri", uri, "credential", "user:correct-secret"))
Defensive patterns

Strategy: retry

Validate before calling

// Validate credential shape and endpoint before use
Preconditions.checkArgument(credential.contains(":"), "credential must be client-id:client-secret");
new URL(oauth2ServerUri.toString()).toURI(); // reachable, well-formed

Try / catch

try {
  OAuth2Util.refresh(session);
} catch (RuntimeException e) {
  // re-authenticate from scratch with fresh credentials, not the stale token
  session = catalog.newSessionWithFreshCredentials();
}

Prevention

When it happens

Trigger: The token endpoint returns an error on every retry: expired/revoked refresh token that cannot be exchanged, wrong client credentials, network failure to the auth server, or the auth server rejecting the grant (e.g. token exchanged from a parent context token that is invalid).

Common situations: Expired refresh token after long downtime; rotated client secrets not updated in catalog properties; auth server outage or 5xx; clock skew causing premature expiry; refresh token single-use and consumed by another process.

Related errors


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/d85cd64f7e96258f. Report an issue: GitHub.

Appendix: source

Thrown at core/src/main/java/org/apache/iceberg/rest/auth/OAuth2Util.java:512

    /**
     * Attempt to refresh the session token using the token exchange flow.
     *
     * @param client a RESTClient
     * @return interval to wait before calling refresh again, or null if no refresh is needed
     */
    public Pair<Integer, TimeUnit> refresh(RESTClient client) {
      if (token() != null && config.keepRefreshed()) {
        AtomicReference<OAuthTokenResponse> ref = new AtomicReference<>(null);
        boolean isSuccessful =
            Tasks.foreach(ref)
                .suppressFailureWhenFinished()
                .retry(tokenRefreshNumRetries)
                .onFailure(
                    (holder, err) -> {
                      // attempt to refresh using the client credential instead of the parent token
                      holder.set(refreshExpiredToken(client));
                      if (holder.get() == null) {
                        LOG.warn("Failed to refresh token", err);
                      }
                    })
                .exponentialBackoff(
                    COMMIT_MIN_RETRY_WAIT_MS_DEFAULT,
                    COMMIT_MAX_RETRY_WAIT_MS_DEFAULT,
                    COMMIT_TOTAL_RETRY_TIME_MS_DEFAULT,
                    2.0 /* exponential */)
                .run(holder -> holder.set(refreshCurrentToken(client)));

        if (!isSuccessful || ref.get() == null) {
          return null;
        }

        OAuthTokenResponse response = ref.get();
        this.config =
            AuthConfig.builder()
                .from(config())
                .token(response.token())

View on GitHub (pinned to 86d9c8fc54)