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
- Re-obtain fresh credentials: fix client-credentials (client-id/client-secret) or refresh token in catalog properties and restart
- Verify network/DNS/proxy connectivity to the token endpoint and that the OAuth2 server URL is correct
- Check auth server logs/auditing for why the grant is rejected (invalid_grant, revoked token)
- Increase tokenRefreshNumRetries / retry window if the failure is transient
- 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
- Rotate and verify client secrets in catalog properties before deployment
- Monitor refresh-failure warnings and alert before tokens fully expire
- Ensure only one process consumes a single-use refresh token
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
- Failed to refresh Google access token
- Invalid credential:
- Not authorized
- Not authorized
- Cannot commit due to unexpected exception
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)