Skip to content

fix: clear async workload refresh state after sync failures - #877

Open
sylvesterkaczmarek wants to merge 2 commits into
openai:mainfrom
sylvesterkaczmarek:fix/workload-identity-sync-provider-failure
Open

fix: clear async workload refresh state after sync failures#877
sylvesterkaczmarek wants to merge 2 commits into
openai:mainfrom
sylvesterkaczmarek:fix/workload-identity-sync-provider-failure

Conversation

@sylvesterkaczmarek

Copy link
Copy Markdown

Summary

Ensure WorkloadIdentityAuth.getTokenAsync() clears its shared refresh state when a custom SubjectTokenProvider.getTokenAsync() throws synchronously instead of returning a future.

Fixes #852.

Problem

getTokenAsync() stores a new refreshInFlight future before it calls the provider. If getTokenAsync() on the provider throws synchronously, the exception escapes before any whenComplete callback is registered.

That leaves refreshInFlight pointing at a future that will never complete.

For a foreground refresh, the first caller sees the synchronous exception and every later caller waits on the abandoned shared future indefinitely. The same state leak can occur when an expiring cached token triggers a background refresh.

Fix

Centralize async refresh startup in startAsyncRefresh().

The helper:

  • calls refreshTokenAsync() inside a try block;
  • on a synchronous exception, routes the error through the existing finishRefresh() path so refreshInFlight is cleared and waiters are completed;
  • returns an exceptionally completed CompletableFuture to the foreground caller instead of throwing before a future is returned;
  • preserves the background-refresh contract by allowing the caller to continue using the still-valid cached token.

Asynchronous provider and token-exchange failures continue through the existing completion callback unchanged.

Regression coverage

Added focused tests for both affected paths:

  1. Foreground refresh: a provider that throws synchronously causes each getTokenAsync() call to return a failed future, and a second call starts a new refresh instead of hanging on stale state.
  2. Background refresh: after seeding a still-valid but expiring token, synchronous provider failures do not prevent the cached token from being returned and do not block a later background-refresh attempt.

Validation

  • branch is based directly on current upstream main at 6a46d024ed67e2be889a4c729837a664d5e7902c;
  • branch is 0 commits behind upstream;
  • production diff is 15 additions / 10 deletions in WorkloadIdentityAuth.kt;
  • changes are limited to the workload-identity refresh lifecycle and focused tests.

Full repository validation is left to GitHub Actions because this environment does not have a complete local checkout/toolchain for the repository.

Risk

Low. Normal successful refreshes and asynchronously completed failures keep their existing flow. The behavior changes only when refresh startup itself throws before returning a CompletableFuture.

@sylvesterkaczmarek
sylvesterkaczmarek requested a review from a team as a code owner August 18, 2026 13:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: clear WorkloadIdentityAuth refresh state after synchronous provider failures

1 participant