{"record":{"id":"fcfb955e9b7d0418","repo":"apache/iceberg","slug":"failed-to-refresh-google-access-token","errorCode":null,"errorMessage":"Failed to refresh Google access token","messagePattern":"Failed to refresh Google access token","errorType":"exception","errorClass":"UncheckedIOException","httpStatus":null,"severity":"error","filePath":"gcp/src/main/java/org/apache/iceberg/gcp/auth/GoogleAuthSession.java","lineNumber":82,"sourceCode":"\n      if (token != null && token.getTokenValue() != null) {\n        HTTPHeaders newHeaders =\n            request\n                .headers()\n                .putIfAbsent(\n                    HTTPHeaders.of(\n                        HTTPHeaders.HTTPHeader.of(\n                            \"Authorization\", \"Bearer \" + token.getTokenValue())));\n        return newHeaders.equals(request.headers())\n            ? request\n            : ImmutableHTTPRequest.builder().from(request).headers(newHeaders).build();\n      } else {\n        throw new IllegalStateException(\n            \"Failed to obtain Google access token. Cannot authenticate request.\");\n      }\n    } catch (IOException e) {\n      LOG.error(\"IOException while trying to refresh Google access token\", e);\n      throw new UncheckedIOException(\"Failed to refresh Google access token\", e);\n    }\n  }\n\n  /**\n   * Closes the session. This is a no-op for GoogleAuthSession as the lifecycle of GoogleCredentials\n   * is not managed by this session.\n   */\n  @Override\n  public void close() {\n    // No-op\n  }\n}\n","sourceCodeStart":64,"sourceCodeEnd":95,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/gcp/src/main/java/org/apache/iceberg/gcp/auth/GoogleAuthSession.java#L64-L95","documentation":"In GoogleAuthSession.authenticate, refreshing the Google credentials can throw IOException (network failure, auth server error, revoked key). The exception is logged and rethrown as UncheckedIOException('Failed to refresh Google access token').","triggerScenarios":"authenticate(request) triggers token refresh (refreshIfExpired/refresh) and the HTTP call to Google's OAuth endpoints fails or the credential's token endpoint rejects the request.","commonSituations":"Network egress to oauth2.googleapis.com blocked, expired/revoked service-account key, incorrect audience or scopes, transient 5xx from Google's token endpoint, or DNS/proxy failures.","solutions":["Check network access to oauth2.googleapis.com from the runtime environment","Verify the service-account key is still valid and not revoked in IAM","Retry with backoff for transient network/5xx errors before failing the request","Confirm scopes/audience configuration in GoogleAuthManager matches the API being called"],"exampleFix":"// before\nrequest = session.authenticate(request); // throws on first transient refresh failure\n// after\ntry {\n  request = session.authenticate(request);\n} catch (UncheckedIOException e) {\n  Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS);\n  request = session.authenticate(request); // retry once\n}","handlingStrategy":"retry","validationCode":"// verify token endpoint reachable\nHttpResponse<String> r = HttpClient.newHttpClient().send(RequestBuilder.get(\"https://oauth2.googleapis.com/token\").build(), BodyHandlers.ofString());","typeGuard":null,"tryCatchPattern":"try { request = session.authenticate(request); } catch (UncheckedIOException e) { /* retry with backoff, then surface */ Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); request = session.authenticate(request); }","preventionTips":["Add bounded retry with backoff for transient refresh failures","Allow network egress to oauth2.googleapis.com","Rotate/verify service-account keys before expiry"],"tags":["gcp","authentication","token","network","retry"],"backgroundTag":"oauth-token-exchange-failed","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}